start building source positioning wizard for superusers
This commit is contained in:
parent
ed0d35a105
commit
6f25708f46
3 changed files with 231 additions and 5 deletions
|
@ -8,8 +8,8 @@ from django.conf import settings
|
||||||
from django.core.cache import cache
|
from django.core.cache import cache
|
||||||
from django.core.exceptions import FieldDoesNotExist
|
from django.core.exceptions import FieldDoesNotExist
|
||||||
from django.db.models import Q
|
from django.db.models import Q
|
||||||
from django.forms import (BooleanField, CharField, ChoiceField, Form, ModelChoiceField, ModelForm, MultipleChoiceField,
|
from django.forms import (BooleanField, CharField, ChoiceField, DecimalField, Form, ModelChoiceField, ModelForm,
|
||||||
Select, ValidationError)
|
MultipleChoiceField, Select, ValidationError)
|
||||||
from django.forms.widgets import HiddenInput
|
from django.forms.widgets import HiddenInput
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from shapely.geometry.geo import mapping
|
from shapely.geometry.geo import mapping
|
||||||
|
@ -52,6 +52,26 @@ class EditorFormBase(I18nModelFormMixin, ModelForm):
|
||||||
all_names = set(os.listdir(settings.SOURCES_ROOT))
|
all_names = set(os.listdir(settings.SOURCES_ROOT))
|
||||||
self.fields['name'].widget = Select(choices=tuple((s, s) for s in sorted(all_names-used_names)))
|
self.fields['name'].widget = Select(choices=tuple((s, s) for s in sorted(all_names-used_names)))
|
||||||
|
|
||||||
|
self.fields['fixed_x'] = DecimalField(label='fixed x', required=False,
|
||||||
|
max_digits=7, decimal_places=3, initial=0)
|
||||||
|
self.fields['fixed_y'] = DecimalField(label='fixed y', required=False,
|
||||||
|
max_digits=7, decimal_places=3, initial=0)
|
||||||
|
self.fields['scale_x'] = DecimalField(label='scale x (m/px)', required=False,
|
||||||
|
max_digits=7, decimal_places=3, initial=1)
|
||||||
|
self.fields['scale_y'] = DecimalField(label='scale y (m/px)', required=False,
|
||||||
|
max_digits=7, decimal_places=3, initial=1)
|
||||||
|
self.fields['lock_aspect'] = BooleanField(label='lock aspect ratio', required=False, initial=True)
|
||||||
|
self.fields['lock_scale'] = BooleanField(label='lock scale (for moving)', required=False, initial=True)
|
||||||
|
|
||||||
|
self.fields.move_to_end('lock_scale', last=False)
|
||||||
|
self.fields.move_to_end('lock_aspect', last=False)
|
||||||
|
self.fields.move_to_end('scale_y', last=False)
|
||||||
|
self.fields.move_to_end('scale_x', last=False)
|
||||||
|
self.fields.move_to_end('fixed_y', last=False)
|
||||||
|
self.fields.move_to_end('fixed_x', last=False)
|
||||||
|
self.fields.move_to_end('access_restriction', last=False)
|
||||||
|
self.fields.move_to_end('name', last=False)
|
||||||
|
|
||||||
if self._meta.model.__name__ == 'AccessRestriction':
|
if self._meta.model.__name__ == 'AccessRestriction':
|
||||||
AccessRestrictionGroup = self.request.changeset.wrap_model('AccessRestrictionGroup')
|
AccessRestrictionGroup = self.request.changeset.wrap_model('AccessRestrictionGroup')
|
||||||
|
|
||||||
|
|
|
@ -52,7 +52,7 @@ body:not(.map-enabled) #sidebar {
|
||||||
padding:12px 12px 0;
|
padding:12px 12px 0;
|
||||||
margin:auto;
|
margin:auto;
|
||||||
}
|
}
|
||||||
#sidebar .content form .form-group:last-child {
|
#sidebar .content form>.form-group:last-child {
|
||||||
margin-bottom:0;
|
margin-bottom:0;
|
||||||
padding:0 0 12px;
|
padding:0 0 12px;
|
||||||
position:sticky;
|
position:sticky;
|
||||||
|
@ -61,6 +61,18 @@ body:not(.map-enabled) #sidebar {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
box-shadow: 0 0 10px 10px #ffffff;
|
box-shadow: 0 0 10px 10px #ffffff;
|
||||||
}
|
}
|
||||||
|
#sidebar .form-group-group {
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
#sidebar .form-group-group .form-group {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
#sidebar .form-group-group .form-group:not(:last-child) {
|
||||||
|
margin-right: 15px;
|
||||||
|
}
|
||||||
|
#sidebar form:not(.show-source-wizard) .source-wizard {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
#noscript {
|
#noscript {
|
||||||
font-weight:bold;
|
font-weight:bold;
|
||||||
color:red;
|
color:red;
|
||||||
|
|
|
@ -9,7 +9,7 @@ editor = {
|
||||||
renderer: L.svg({ padding: 2 }),
|
renderer: L.svg({ padding: 2 }),
|
||||||
zoom: 2,
|
zoom: 2,
|
||||||
maxZoom: 10,
|
maxZoom: 10,
|
||||||
minZoom: 0,
|
minZoom: -5,
|
||||||
crs: L.CRS.Simple,
|
crs: L.CRS.Simple,
|
||||||
editable: true,
|
editable: true,
|
||||||
zoomSnap: 0
|
zoomSnap: 0
|
||||||
|
@ -150,6 +150,51 @@ editor = {
|
||||||
$('#navbar-collapse').find('.nav').html(nav.html());
|
$('#navbar-collapse').find('.nav').html(nav.html());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (editor._source_image_layer) {
|
||||||
|
editor._source_image_layer.remove();
|
||||||
|
editor._source_image_layer = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var group;
|
||||||
|
if (content.find('[name=fixed_x]')) {
|
||||||
|
$('[name=name]').change(editor._source_name_selected).change();
|
||||||
|
if (!content.find('[data-new]').length) {
|
||||||
|
var bounds = [[parseFloat(content.find('[name=left]').val()), parseFloat(content.find('[name=bottom]').val())], [parseFloat(content.find('[name=right]').val()), parseFloat(content.find('[name=top]').val())]];
|
||||||
|
bounds = L.GeoJSON.coordsToLatLngs(bounds);
|
||||||
|
editor.map.fitBounds(bounds, {padding: [30, 50]});
|
||||||
|
}
|
||||||
|
|
||||||
|
group = $('<div class="form-group-group source-wizard">');
|
||||||
|
group.insertBefore(content.find('[name=fixed_x]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=fixed_x]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=fixed_y]').closest('.form-group'));
|
||||||
|
|
||||||
|
group = $('<div class="form-group-group source-wizard">');
|
||||||
|
group.insertBefore(content.find('[name=scale_x]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=scale_x]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=scale_y]').closest('.form-group'));
|
||||||
|
|
||||||
|
content.find('[name=left], [name=bottom], [name=right], [name=top]').change(editor._source_image_bounds_changed);
|
||||||
|
content.find('[name=scale_x], [name=scale_y]').change(editor._source_image_scale_changed);
|
||||||
|
content.find('[name=left], [name=bottom], [name=right], [name=top]').each(function() { $(this).data('oldval', $(this).val()); });
|
||||||
|
|
||||||
|
content.find('[name=lock_aspect], [name=lock_scale]').closest('.form-group').addClass('source-wizard');
|
||||||
|
|
||||||
|
var source_width = (parseFloat(content.find('[name=right]').val()) || 0) - (parseFloat(content.find('[name=left]').val()) || 0),
|
||||||
|
source_height = (parseFloat(content.find('[name=top]').val()) || 0) - (parseFloat(content.find('[name=bottom]').val()) || 0);
|
||||||
|
editor._source_aspect_ratio = source_width/(source_height || 1);
|
||||||
|
}
|
||||||
|
if (content.find('[name=left]')) {
|
||||||
|
group = $('<div class="form-group-group">');
|
||||||
|
group.insertBefore(content.find('[name=left]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=left]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=top]').closest('.form-group'));
|
||||||
|
group = $('<div class="form-group-group">');
|
||||||
|
group.insertBefore(content.find('[name=right]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=right]').closest('.form-group'));
|
||||||
|
group.append(content.find('[name=bottom]').closest('.form-group'));
|
||||||
|
}
|
||||||
|
|
||||||
content.find('[data-toggle="tooltip"]').tooltip();
|
content.find('[data-toggle="tooltip"]').tooltip();
|
||||||
|
|
||||||
var modal_close = content.find('[data-modal-close]');
|
var modal_close = content.find('[data-modal-close]');
|
||||||
|
@ -292,6 +337,148 @@ editor = {
|
||||||
$.post(action, data, editor._sidebar_loaded).fail(editor._sidebar_error);
|
$.post(action, data, editor._sidebar_loaded).fail(editor._sidebar_error);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_source_image_orig_width: 0,
|
||||||
|
_source_image_orig_height: 0,
|
||||||
|
_source_image_aspect_ratio: 0,
|
||||||
|
_source_image_untouched: 0,
|
||||||
|
_source_image_layer: null,
|
||||||
|
_source_name_selected: function() {
|
||||||
|
if (editor._source_image_layer) {
|
||||||
|
editor._source_image_layer.remove();
|
||||||
|
editor._source_image_layer = null;
|
||||||
|
}
|
||||||
|
$('<img src="/editor/sourceimage/'+$(this).val()+'">').on('load', editor._source_name_selected_ajax_callback);
|
||||||
|
$('#sidebar form').removeClass('show-source-wizard');
|
||||||
|
$('body').removeClass('map-enabled');
|
||||||
|
},
|
||||||
|
_source_name_selected_ajax_callback: function() {
|
||||||
|
if ($(this).attr('src').endsWith($('#sidebar [name=name]').val())) {
|
||||||
|
$('#sidebar form').addClass('show-source-wizard');
|
||||||
|
$(this).appendTo('body').hide();
|
||||||
|
editor._source_image_orig_width = $(this).width();
|
||||||
|
editor._source_image_orig_height = $(this).height();
|
||||||
|
$(this).remove();
|
||||||
|
$('body').addClass('map-enabled');
|
||||||
|
var content = $('#sidebar');
|
||||||
|
if (content.find('[data-new]').length || isNaN(parseFloat(content.find('[name=right]').val())) || isNaN(parseFloat(content.find('[name=left]').val())) || isNaN(parseFloat(content.find('[name=top]').val())) || isNaN(parseFloat(content.find('[name=bottom]').val()))) {
|
||||||
|
editor._source_aspect_ratio = $(this).width()/$(this).height();
|
||||||
|
content.find('[name=left]').val(0).data('oldval', 0);
|
||||||
|
content.find('[name=bottom]').val(0).data('oldval', 0);
|
||||||
|
var factor = 1;
|
||||||
|
while(factor < 1000 && (editor._source_image_orig_width/factor)>1500) {
|
||||||
|
factor *= 10;
|
||||||
|
}
|
||||||
|
var width = (editor._source_image_orig_width/factor).toFixed(2),
|
||||||
|
height = (editor._source_image_orig_height/factor).toFixed(2);
|
||||||
|
content.find('[name=right]').val(width).data('oldval', width);
|
||||||
|
content.find('[name=top]').val(height).data('oldval', height);
|
||||||
|
content.find('[name=scale_x]').val(1/factor);
|
||||||
|
content.find('[name=scale_y]').val(1/factor);
|
||||||
|
} else {
|
||||||
|
editor._source_image_calculate_scale();
|
||||||
|
}
|
||||||
|
editor._source_image_repositioned();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_source_image_repositioned: function() {
|
||||||
|
var content = $('#sidebar');
|
||||||
|
if (isNaN(parseFloat(content.find('[name=right]').val())) || isNaN(parseFloat(content.find('[name=left]').val())) || isNaN(parseFloat(content.find('[name=top]').val())) || isNaN(parseFloat(content.find('[name=bottom]').val()))) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
var bounds = [[parseFloat(content.find('[name=left]').val()), parseFloat(content.find('[name=bottom]').val())], [parseFloat(content.find('[name=right]').val()), parseFloat(content.find('[name=top]').val())]];
|
||||||
|
bounds = L.GeoJSON.coordsToLatLngs(bounds);
|
||||||
|
|
||||||
|
editor._set_max_bounds(bounds);
|
||||||
|
if (editor._source_image_layer) {
|
||||||
|
editor._source_image_layer.setBounds(bounds)
|
||||||
|
} else {
|
||||||
|
editor._source_image_layer = L.imageOverlay('/editor/sourceimage/'+content.find('[name=name]').val(), bounds, {opacity: 0.3, zIndex: 10000});
|
||||||
|
editor._source_image_layer.addTo(editor.map);
|
||||||
|
if (content.find('[data-new]').length) {
|
||||||
|
editor.map.fitBounds(bounds, {padding: [30, 50]});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_source_image_calculate_scale: function() {
|
||||||
|
var content = $('#sidebar');
|
||||||
|
var source_width = parseFloat(content.find('[name=right]').val()) - parseFloat(content.find('[name=left]').val()),
|
||||||
|
source_height = parseFloat(content.find('[name=top]').val()) - parseFloat(content.find('[name=bottom]').val());
|
||||||
|
if (isNaN(source_width) || isNaN(source_height)) return;
|
||||||
|
var scale_x = (source_width/editor._source_image_orig_width).toFixed(3),
|
||||||
|
scale_y = (source_height/editor._source_image_orig_height).toFixed(3);
|
||||||
|
content.find('[name=scale_x]').val(scale_x);
|
||||||
|
content.find('[name=scale_y]').val(scale_y);
|
||||||
|
if (scale_x !== scale_y) {
|
||||||
|
content.find('[name=lock_aspect]').prop('checked', false);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
_source_image_bounds_changed: function() {
|
||||||
|
var content = $('#sidebar'),
|
||||||
|
lock_scale = content.find('[name=lock_scale]').prop('checked'),
|
||||||
|
oldval = $(this).data('oldval'),
|
||||||
|
newval = $(this).val(),
|
||||||
|
diff = parseFloat(newval)-parseFloat(oldval);
|
||||||
|
$(this).data('oldval', newval);
|
||||||
|
if (lock_scale) {
|
||||||
|
if (!isNaN(diff)) {
|
||||||
|
var other_field_name = {left: 'right', right: 'left', top: 'bottom', bottom: 'top'}[$(this).attr('name')],
|
||||||
|
other_field = content.find('[name='+other_field_name+']'),
|
||||||
|
other_val = parseFloat(other_field.val());
|
||||||
|
if (!isNaN(other_val)) {
|
||||||
|
other_field.val((other_val+diff).toFixed(2)).data('oldval', other_val);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
editor._source_image_calculate_scale();
|
||||||
|
}
|
||||||
|
editor._source_image_repositioned();
|
||||||
|
},
|
||||||
|
_source_image_scale_changed: function() {
|
||||||
|
var content = $('#sidebar'),
|
||||||
|
lock_aspect = content.find('[name=lock_scale]').prop('checked');
|
||||||
|
if (lock_aspect) {
|
||||||
|
var other_field_name = {scale_x: 'scale_y', scale_y: 'scale_x'}[$(this).attr('name')],
|
||||||
|
other_field = content.find('[name='+other_field_name+']');
|
||||||
|
other_field.val($(this).val());
|
||||||
|
}
|
||||||
|
var f_scale_x = content.find('[name=scale_x]'),
|
||||||
|
f_scale_y = content.find('[name=scale_y]'),
|
||||||
|
scale_x = f_scale_x.val(),
|
||||||
|
scale_y = f_scale_y.val(),
|
||||||
|
fixed_x = parseFloat(content.find('[name=fixed_x]').val()),
|
||||||
|
fixed_y = parseFloat(content.find('[name=fixed_y]').val()),
|
||||||
|
left = parseFloat(content.find('[name=left]').val()),
|
||||||
|
bottom = parseFloat(content.find('[name=bottom]').val()),
|
||||||
|
right = parseFloat(content.find('[name=right]').val()),
|
||||||
|
top = parseFloat(content.find('[name=top]').val());
|
||||||
|
|
||||||
|
scale_x = parseFloat(scale_x);
|
||||||
|
scale_y = parseFloat(scale_y);
|
||||||
|
|
||||||
|
if (isNaN(scale_x) || isNaN(scale_y) || isNaN(fixed_x) || isNaN(fixed_y) || isNaN(left) || isNaN(bottom) || isNaN(right) || isNaN(top)) return;
|
||||||
|
|
||||||
|
var fixed_x_relative = (fixed_x-left)/(right-left),
|
||||||
|
fixed_y_relative = (fixed_y-bottom)/(top-bottom),
|
||||||
|
width = editor._source_image_orig_width*scale_x,
|
||||||
|
height = editor._source_image_orig_height*scale_y,
|
||||||
|
left = fixed_x-(width*fixed_x_relative),
|
||||||
|
bottom = fixed_y-(height*fixed_y_relative),
|
||||||
|
right = left+width,
|
||||||
|
top = bottom+height;
|
||||||
|
|
||||||
|
left = left.toFixed(2);
|
||||||
|
bottom = bottom.toFixed(2);
|
||||||
|
right = right.toFixed(2);
|
||||||
|
top = top.toFixed(2);
|
||||||
|
|
||||||
|
content.find('[name=left]').val(left).data('oldval', left);
|
||||||
|
content.find('[name=bottom]').val(bottom).data('oldval', bottom);
|
||||||
|
content.find('[name=right]').val(right).data('oldval', right);
|
||||||
|
content.find('[name=top]').val(top).data('oldval', top);
|
||||||
|
|
||||||
|
editor._source_image_repositioned();
|
||||||
|
},
|
||||||
|
|
||||||
// geometries
|
// geometries
|
||||||
geometrystyles: {},
|
geometrystyles: {},
|
||||||
_loading_geometry: false,
|
_loading_geometry: false,
|
||||||
|
@ -314,6 +501,7 @@ editor = {
|
||||||
_arrow_colors: [],
|
_arrow_colors: [],
|
||||||
_last_vertex: null,
|
_last_vertex: null,
|
||||||
_orig_vertex_pos: null,
|
_orig_vertex_pos: null,
|
||||||
|
_max_bounds: null,
|
||||||
init_geometries: function () {
|
init_geometries: function () {
|
||||||
// init geometries and edit listeners
|
// init geometries and edit listeners
|
||||||
editor._highlight_layer = L.layerGroup().addTo(editor.map);
|
editor._highlight_layer = L.layerGroup().addTo(editor.map);
|
||||||
|
@ -376,13 +564,18 @@ editor = {
|
||||||
editor.geometrystyles = geometrystyles;
|
editor.geometrystyles = geometrystyles;
|
||||||
$.getJSON('/api/editor/bounds/', function(bounds) {
|
$.getJSON('/api/editor/bounds/', function(bounds) {
|
||||||
bounds = L.GeoJSON.coordsToLatLngs(bounds.bounds);
|
bounds = L.GeoJSON.coordsToLatLngs(bounds.bounds);
|
||||||
editor.map.setMaxBounds(bounds);
|
editor._max_bounds = bounds;
|
||||||
|
editor._set_max_bounds();
|
||||||
editor.map.fitBounds(bounds, {padding: [30, 50]});
|
editor.map.fitBounds(bounds, {padding: [30, 50]});
|
||||||
editor.init_sidebar();
|
editor.init_sidebar();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
editor.get_sources();
|
editor.get_sources();
|
||||||
},
|
},
|
||||||
|
_set_max_bounds: function(bounds) {
|
||||||
|
bounds = bounds ? L.latLngBounds(editor._max_bounds[0], editor._max_bounds[1]).extend(bounds) : editor._max_bounds;
|
||||||
|
editor.map.setMaxBounds(bounds);
|
||||||
|
},
|
||||||
_last_geometry_url: null,
|
_last_geometry_url: null,
|
||||||
load_geometries: function (geometry_url, highlight_type, editing_id) {
|
load_geometries: function (geometry_url, highlight_type, editing_id) {
|
||||||
// load geometries from url
|
// load geometries from url
|
||||||
|
@ -400,6 +593,7 @@ editor = {
|
||||||
editor._graph_edges_from = {};
|
editor._graph_edges_from = {};
|
||||||
editor._graph_edges_to = {};
|
editor._graph_edges_to = {};
|
||||||
|
|
||||||
|
editor._set_max_bounds();
|
||||||
$.getJSON(geometry_url, function(geometries) {
|
$.getJSON(geometry_url, function(geometries) {
|
||||||
editor.map.removeLayer(editor._highlight_layer);
|
editor.map.removeLayer(editor._highlight_layer);
|
||||||
editor._highlight_layer.clearLayers();
|
editor._highlight_layer.clearLayers();
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue