refactor the paper size / orientation form step

* pick size / orientation from predefined formats with single click
* allow for custom width/height input
* dynamicly preview paper dimensions / aspect ratio
* IMHO simplifies the javascript side of things
pull/49/head
Hartmut Holzgraefe 2020-02-01 10:55:45 +00:00
rodzic 9eba9b3dc1
commit cc28344c1e
5 zmienionych plików z 298 dodań i 183 usunięć

Wyświetl plik

@ -59,20 +59,12 @@ class MapRenderingJobForm(forms.ModelForm):
'lat_bottom_right', 'lon_bottom_right',
'track', 'umap', 'submittermail')
ORIENTATION = (('portrait', mark_safe("<i class='fa fa-file'></i> %s" % _('Portrait'))),
('landscape', mark_safe("<i class='fa fa-file' style='transform: rotate(-90deg)'></i> %s" % _('Landscape'))))
mode = forms.CharField(initial='bbox', widget=forms.HiddenInput)
layout = forms.ChoiceField(choices=(), widget=forms.RadioSelect(attrs= { 'onchange' : 'clearPaperSize(); $("#layout-preview").attr("src","/media/img/layout/"+this.value+".png");'}))
stylesheet = forms.ChoiceField(choices=(), widget=forms.Select(attrs= { 'onchange' : '$("#style-preview").attr("src","/media/img/style/"+this.value+".jpg");'}))
overlay = forms.MultipleChoiceField(choices=(), widget=forms.SelectMultiple(attrs= { 'class': 'multipleSelect' }), required=False)
papersize = forms.ChoiceField(choices=(), widget=forms.RadioSelect)
default_papersize = forms.CharField(initial='', widget=forms.HiddenInput, required=False)
paperorientation = forms.ChoiceField(choices=ORIENTATION,
widget=forms.RadioSelect)
default_paperorientation = forms.CharField(initial='landscape', widget=forms.HiddenInput, required=False)
paper_width_mm = forms.IntegerField(widget=forms.HiddenInput)
paper_height_mm = forms.IntegerField(widget=forms.HiddenInput)
paper_width_mm = forms.IntegerField(widget=forms.NumberInput(attrs= {'onchange' : 'change_papersize();'}), min_value=100)
paper_height_mm = forms.IntegerField(widget=forms.NumberInput(attrs= {'onchange' : 'change_papersize();'}), min_value=100)
maptitle = forms.CharField(max_length=256, required=False)
bbox = widgets.AreaField(label=_("Area"),
fields=(forms.FloatField(), forms.FloatField(),
@ -179,20 +171,6 @@ class MapRenderingJobForm(forms.ModelForm):
if not self.fields['overlay'].initial:
self.fields['overlay'].initial = ''
def _build_papersize_description(p):
if p[0] == "Best fit":
return mark_safe(_("Best fit <em class=\"papersize\"></em>"))
elif p[0] == "Custom":
return mark_safe(_("Custom: <input disabled id='id_custom_width' name='custom_width' type='number' style='width: 5em' min='0' onchange='custom_size();'/> &times; <input disabled id='id_custom_height' name='custom_height' type='number' style='width: 5em' min='0' onchange='custom_size();'/> mm²"))
else:
return mark_safe("%s <em class=\"papersize\">"
"(%.0f &times; %.0f mm²)</em>"
% (p[0], p[1], p[2]))
self.fields['papersize'].choices = [
(p[0], _build_papersize_description(p))
for p in self._ocitysmap.get_all_paper_sizes()]
def clean(self):
"""Cleanup function for the map query form. Different checks are
required depending on the selected mode (by admininstrative city, or by
@ -213,10 +191,6 @@ class MapRenderingJobForm(forms.ModelForm):
overlay_array.append(overlay)
overlay = ",".join(overlay_array)
if cleaned_data.get("paperorientation") == 'landscape':
cleaned_data["paper_width_mm"], cleaned_data["paper_height_mm"] = \
cleaned_data.get("paper_height_mm"), cleaned_data.get("paper_width_mm")
if layout == '':
msg = _(u"Layout required")
self._errors["layout"] = ErrorList([msg])

Wyświetl plik

@ -152,8 +152,6 @@ $('#error-modal').modal('show')
<form id="mainfrm" method="post" enctype="multipart/form-data" action="{% url "new" %}#submitmapform">
{{ form.mode }}
{{ form.default_papersize }}
{{ form.default_paperorientation }}
<div class="row" style="margin-top: 30px;" >
<div class="col-lg-12" >
@ -350,20 +348,22 @@ $('#error-modal').modal('show')
</div>
<div class="tab" id="wizard-step-paper-size">
<div class="row">
<div class="row" id="paper-size">
<div class="col-lg-6">
<fieldset id="paper-size">
<legend>{% trans "Paper size" %}{% trans "(width x height)" %}</legend>
{{ form.papersize }}
<fieldset>
<legend>{% trans "Paper size" %} {% trans "(width x height)" %}</legend>
{{ form.paper_width_mm }}
{{ form.paper_height_mm }}
</fieldset>
<div height="1ex">&nbsp;</div>
<canvas id="paper_canvas" width="400" height="400">
</canvas>
</div>
<div class="col-lg-6">
<fieldset id="paper-orientation">
<legend>{% trans "Paper orientation" %}</legend>
{{ form.paperorientation }}
</fieldset>
<fieldset>
<legend>Paper size suggestions</legend>
{{ papersize_suggestions }}
</fieldset>
</div>
</div>

Wyświetl plik

@ -15,7 +15,6 @@ function preparePaperSize() {
$('#paper-wait').show();
$('#paper-size').hide();
$('#paper-orientation').hide();
$('#paper-size-loading-error').hide();
$('#paper-size-loading').show();
$('#nextlink').hide();
@ -45,154 +44,281 @@ function preparePaperSize() {
}
$.ajax('/apis/papersize/', { type: 'post', data: args })
.fail(function() { $('#paper-size-loading-error').show(); })
.always(function() { $('#paper-size-loading').hide(); })
.done(function(data) {
.fail(function() { $('#paper-size-loading-error').show(); })
.always(function() { $('#paper-size-loading').hide(); })
.done(function(data) {
var i;
function get_paper_def(paper) {
for (i in data) {
if (paper == data[i]['name']) {
return data[i];
}
}
disable_all_papersizes();
return null;
}
for (i in data) {
var w = data[i]['width'];
var h = data[i]['height'];
function handle_paper_size_click(w, h, p_ok, l_ok, l_preferred) {
if (w == 0 && h == 0) { // Custom
$('label input', custom_paper_size).prop('checked', true);
custom_size();
$('#id_custom_width').prop( "disabled", false);
$('#id_custom_height').prop( "disabled", false);
$('input[value=portrait]').prop("disabled", true);
$('input[value=landscape]').prop("disabled", true);
return;
}
else
{
$('#id_custom_width').prop( "disabled", true);
$('#id_custom_height').prop( "disabled", true);
$('input[value=portrait]').prop("disabled", false);
$('input[value=landscape]').prop("disabled", false);
}
var l = $('#paper-orientation input[value=landscape]');
var p = $('#paper-orientation input[value=portrait]');
if (data[i]['name'] == 'Best fit') {
// update min width/height input limits
$('#id_paper_width_mm').attr("min", w);
$('#id_paper_height_mm').attr("min", h);
if (l_ok) {
l.removeAttr('disabled');
if (!p_ok) { l.attr('checked', 'checked'); }
} else {
l.attr('disabled', 'disabled');
p.attr('checked', 'checked');
}
$("#ww").text(w);
$("#hh").text(h);
if (p_ok) {
p.removeAttr('disabled');
if (!l_ok) { p.attr('checked', 'checked'); }
} else {
p.attr('disabled', 'disabled');
l.attr('checked', 'checked');
}
enable_papersize(0,0);
if (l_ok && p_ok) {
if (l_preferred) {
l.attr('checked', 'checked');
} else {
p.attr('checked', 'checked');
}
}
$('#id_paper_width_mm').val(w.toFixed(0));
$('#id_paper_height_mm').val(h.toFixed(0));
}
// if current values are below limits -> enforce min size
if (w > parseInt($('#id_paper_width_mm').val())
|| h > parseInt($('#id_paper_height_mm').val())) {
$('#id_paper_width_mm').val(w);
$('#id_paper_height_mm').val(h);
}
continue;
}
var preferrred_paper_size = null;
var custom_paper_size = null;
var default_paper_size = null;
var default_paper_orientation = 'landscape';
if (data[i]['portrait_ok']) {
enable_papersize(w,h);
}
if (data[i]['landscape_ok']) {
enable_papersize(h,w);
}
}
$.each($('#paper-size ul li'), function(i, item) {
$(item).hide();
var input = $('label input[value]', item);
var paper = input.val();
var def = get_paper_def(paper);
if (def) {
$('label', item).bind('click', function() {
handle_paper_size_click(def['width'], def['height'], def['portrait_ok'], def['landscape_ok'], def['landscape_preferred']);
});
set_papersize($('#id_paper_width_mm').val(),$('#id_paper_height_mm').val());
if (def['default']) { // preferred paper size returned by API
preferrred_paper_size = $(item);
}
if ($('#id_default_papersize').val() == paper) {
default_paper_size = $(item);
default_paper_orientation = $('#id_default_paperorientation').val();
}
$(item).show();
// TODO: fix for i18n
if (paper == 'Best fit') {
w = def['width'];
w = w.toFixed(0);
h = def['height'];
h = h.toFixed(0);
$('label em.papersize', item).html('(' + w + ' &times; ' + h + ' mm²)');
$("#id_custom_width").val(w);
$("#id_custom_height").val(h);
$("#id_custom_width").prop('min',w);
$("#id_custom_height").prop('min',h);
}
if (paper == 'Custom') {
custom_paper_size = $(item);
}
}
});
if (default_paper_size) {
$('label input', default_paper_size).click();
// TODO: really remember orientation? or go with aspect ratio?
if (default_paper_orientation) {
$('#paper-selection input[value='+default_paper_orientation+']').click();
}
} else if(preferrred_paper_size) {
$('label input', preferrred_paper_size).click();
}
$('#paper-wait').hide();
$('#paper-size').show();
$('#paper-orientation').show();
papersize_prepared=true;
$('#nextlink').show();
$('#paper-wait').hide();
$('#paper-size').show();
return null;
});
}
function custom_size()
function change_papersize()
{
w = $("#id_custom_width");
h = $("#id_custom_height");
w = parseInt($('#id_paper_width_mm').val());
h = parseInt($('#id_paper_height_mm').val());
wmin = parseInt($('#id_paper_width_mm').attr('min'));
hmin = parseInt($('#id_paper_height_mm').attr('min'));
if (w < wmin) {
h = hmin;
}
if (h < hmin) {
h = hmin;
}
if (w.val() < w.prop('min')) {
w.val(w.prop('min'));
}
set_papersize(w,h);
}
if (h.val() < h.prop('min')) {
h.val(h.prop('min'));
}
function set_papersize(w,h)
{
$('.papersize:enabled').each(function(index) {
$(this)[0].classList.remove("btn-success");
$(this)[0].classList.add("btn-primary");
});
if (w.val() > h.val()) {
$('input[value=landscape]').prop('checked', true);
$("#id_paper_width_mm").val(h.val());
$("#id_paper_height_mm").val(w.val());
var id;
if (w >0 && h>0) {
id = "#paper_" + w + "_" + h;
} else {
$('input[value=portrait]').prop('checked', true);
$("#id_paper_width_mm").val(w.val());
$("#id_paper_height_mm").val(h.val());
id = "#paper_best_fit";
w = parseInt($("#ww").text());
h = parseInt($("#hh").text());
}
var button = $(id);
if (button.length) {
button[0].classList.remove("btn-primary");
button[0].classList.add("btn-success");
}
$("#id_paper_width_mm").val(w);
$("#id_paper_height_mm").val(h);
var canvas = document.getElementById('paper_canvas');
// canvas.width = canvas.height * (canvas.clientWidth / canvas.clientHeight);
var ctx = canvas.getContext("2d");
var cw = canvas.width;
var ch = canvas.height;
w = parseInt(w);
h = parseInt(h);
if (w > h) {
tw = (cw - 20)
dw = 20;
th = tw * (h/w);
dh = (ch-th)/2;
} else {
th = (ch - 20)
dh = 20;
tw = th * (w/h);
dw = (cw-tw)/2;
}
ctx.clearRect(0, 0, cw, ch);
ctx.strokeStyle = "#000000";
ctx.setLineDash([]);
// the actual paper size rectangle
ctx.strokeRect(dw, dh, tw, th);
ctx.strokeStyle = "#00007F";
ctx.fillStyle = "#00007F";
// height scale
// top edge
ctx.beginPath();
ctx.moveTo(dw - 20, dh);
ctx.lineTo(dw - 10, dh);
ctx.stroke();
// top arrow tip
ctx.beginPath();
ctx.moveTo(dw - 15, dh);
ctx.lineTo(dw - 20, dh+10);
ctx.lineTo(dw - 10, dh+10);
ctx.fill();
// upper half of height bar
ctx.beginPath();
ctx.moveTo(dw - 15, dh+10);
ctx.lineTo(dw - 15, dh+th/2-20);
ctx.stroke();
// height text
ctx.font = '10px serif';
ctx.textAlign = 'center';
ctx.save();
ctx.translate(dw-10, dh+th/2);
ctx.rotate(-Math.PI/2);
ctx.fillText(h, 0, 0);
ctx.restore();
// lower half of height bar
ctx.beginPath();
ctx.moveTo(dw - 15, dh+th-10);
ctx.lineTo(dw - 15, dh+th/2+20);
ctx.stroke();
// bottom arrow tip
ctx.beginPath();
ctx.moveTo(dw - 15, dh+th);
ctx.lineTo(dw - 20, dh+th-10);
ctx.lineTo(dw - 10, dh+th-10);
ctx.fill();
// bottom edge
ctx.beginPath();
ctx.moveTo(dw - 20, dh+th);
ctx.lineTo(dw - 10, dh+th);
ctx.stroke();
// left edge
ctx.beginPath();
ctx.moveTo(dw, dh-20);
ctx.lineTo(dw, dh-10);
ctx.stroke();
// left arrow tip
ctx.beginPath();
ctx.moveTo(dw, dh - 15);
ctx.lineTo(dw+10, dh - 20);
ctx.lineTo(dw+10, dh - 10);
ctx.fill();
// left half of width bar
ctx.beginPath();
ctx.moveTo(dw+10, dh - 15);
ctx.lineTo(dw+tw/2-20, dh - 15);
ctx.stroke();
// width text
ctx.font = '10px serif';
ctx.textAlign = 'center';
ctx.fillText(w, dw+tw/2, dh - 10);
// right half of width bar
ctx.beginPath();
ctx.moveTo(dw+tw-10, dh - 15);
ctx.lineTo(dw+tw/2+20, dh - 15);
ctx.stroke();
// right arrow tip
ctx.beginPath();
ctx.moveTo(dw+tw, dh - 15);
ctx.lineTo(dw+tw-10, dh - 20);
ctx.lineTo(dw+tw-10, dh - 10);
ctx.fill();
// right edge
ctx.beginPath();
ctx.moveTo(dw+tw, dh - 20);
ctx.lineTo(dw+tw, dh - 10);
ctx.stroke();
// dashed paper diagonals
ctx.beginPath();
ctx.strokeStyle = "#000000";
ctx.setLineDash([4, 2]);
ctx.moveTo(dw, dh);
ctx.lineTo(dw+tw, dh+th);
ctx.moveTo(dw, dh+th);
ctx.lineTo(dw+tw, dh);
ctx.stroke();
$('#nextlink').show();
}
function disable_all_papersizes()
{
$('.papersize:enabled').each(function(index) {
$(this)[0].classList.remove("btn-success");
$(this)[0].classList.remove("btn-primary");
$(this)[0].classList.add("btn-outline-secondary");
$(this)[0].setAttribute("disabled", "");
});
}
function disable_papersize(w,h)
{
var id = "#paper_" + w + "_" + h;
var button = $(id);
if (button.length) {
button[0].classList.remove("btn-primary");
button[0].classList.remove("btn-success");
button[0].classList.add("btn-outline-secondary");
button[0].setAttribute("disabled", "");
}
}
function enable_papersize(w,h)
{
var id;
if (w >0 && h>0) {
id = "#paper_" + w + "_" + h;
} else {
id = "#paper_best_fit";
w = parseInt($("#ww").text());
h = parseInt($("#hh").text());
}
var button = $(id);
if (button.length) {
button[0].classList.remove("btn-success");
button[0].classList.remove("btn-outline-secondary");
button[0].classList.add("btn-primary");
button[0].removeAttribute("disabled");
}
}

Wyświetl plik

@ -268,13 +268,11 @@ function prepareLangTitle() {
$('#summary-overlay').text(overlay_str.slice(0,-2));
var paper_text = $('input[name=papersize]:checked').val().trim();
paper_text = paper_text + ', ' + ( $('input[value=landscape]').is(':checked')
? '{% trans "Landscape" %}'
: '{% trans "Portrait" %}' );
paper_text = paper_text + ' (' + $('#id_paper_width_mm').val();
paper_text = paper_text + ' x ' + $('#id_paper_height_mm').val() + ' mm²)';
var paper_text = "";
paper_text = paper_text + $('#id_paper_width_mm').val();
paper_text = paper_text + ' x ' + $('#id_paper_height_mm').val() + ' mm²';
// TODO re-add paper size name and orientation where applicable
$('input[value=portrait]').prop("disabled", false);
$('input[value=landscape]').prop("disabled", false);

Wyświetl plik

@ -41,6 +41,7 @@ from django.forms.models import model_to_dict
from django.core.exceptions import ValidationError
from django.urls import get_script_prefix
from django.db import connections
from django.utils.safestring import mark_safe
import ocitysmap
from www.maposmatic import helpers, forms, nominatim, models
@ -127,14 +128,16 @@ def donate_thanks(request):
def new(request):
"""The map creation page and form."""
papersize_buttons = ''
if request.method == 'POST':
form = forms.MapRenderingJobForm(request.POST, request.FILES)
if form.is_valid():
request.session['new_layout'] = form.cleaned_data.get('layout')
request.session['new_stylesheet'] = form.cleaned_data.get('stylesheet')
request.session['new_overlay'] = form.cleaned_data.get('overlay')
request.session['new_papersize'] = form.cleaned_data.get('papersize')
request.session['new_paperorientation'] = form.cleaned_data.get('paperorientation')
request.session['new_paper_width_mm'] = form.cleaned_data.get('paper_width_mm')
request.session['new_paper_height_mm'] = form.cleaned_data.get('paper_height_mm')
job = form.save(commit=False)
job.administrative_osmid = form.cleaned_data.get('administrative_osmid')
@ -177,15 +180,29 @@ def new(request):
if not 'overlay' in init_vals and 'new_overlay' in request.session:
init_vals['overlay'] = request.session['new_overlay']
if not 'papersize' in init_vals and 'new_papersize' in request.session:
init_vals['default_papersize'] = request.session['new_papersize']
if not 'paper_width_mm' in init_vals and 'new_paper_width_mm' in request.session:
init_vals['paper_width_mm'] = request.session['new_paper_width_mm']
if not 'paper_orientation' in init_vals and 'new_paperorientation' in request.session:
init_vals['default_paperorientation'] = request.session['new_paperorientation']
if not 'paper_height_mm' in init_vals and 'new_paper_width_mm' in request.session:
init_vals['paper_height_mm'] = request.session['new_paper_height_mm']
form = forms.MapRenderingJobForm(initial=init_vals)
return render(request, 'maposmatic/new.html', { 'form' : form })
_ocitysmap = ocitysmap.OCitySMap(www.settings.OCITYSMAP_CFG_PATH)
papersize_buttons += "<p><button id='paper_best_fit' type='button' class='btn btn-primary papersize' onclick='set_papersize(0,0);'><i class='fas fa-square fa-2x'></i></button> <b>Best fit</b> (<span id='ww'>?</span>x<span id='hh'>?</span>mm²)</p>"
for p in _ocitysmap.get_all_paper_sizes():
if p[1] is not None:
papersize_buttons += "<p>"
if p[1] != p[2]:
papersize_buttons += "<button id='paper_%d_%d' type='button' class='btn btn-primary papersize' onclick='set_papersize(%s, %s);'><i class='fas fa-portrait fa-2x'></i></button> " % (p[1], p[2], p[1], p[2])
papersize_buttons += "<button id='paper_%d_%d' type='button' class='btn btn-primary papersize' onclick='set_papersize(%s, %s);'><i class='fas fa-image fa-2x'></i></button> " % (p[2], p[1], p[2], p[1])
else:
papersize_buttons += "<button id='paper_%d_%d' disabled type='button' class='btn btn-primary papersize' onclick='set_papersize(%s, %s);'><i class='fas fa-square fa-2x'></i></button> " % (p[1], p[2], p[1], p[2])
papersize_buttons += "<b>%s</b> (%sx%smm²)</p>" % (p[0], repr(p[1]), repr(p[2]))
return render(request, 'maposmatic/new.html', { 'form' : form , 'papersize_suggestions': mark_safe(papersize_buttons)})
def map_full(request, id, nonce=None):
"""The full-page map details page.