start building source positioning wizard for superusers

This commit is contained in:
Laura Klünder 2018-11-17 01:40:57 +01:00
parent ed0d35a105
commit 6f25708f46
3 changed files with 231 additions and 5 deletions

View file

@ -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')

View file

@ -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;

View file

@ -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();