Merge branch 'site-osm-baustelle' of https://github.com/hholzgra/maposmatic into site-osm-baustelle
Przed Szerokość: | Wysokość: | Rozmiar: 552 KiB Po Szerokość: | Wysokość: | Rozmiar: 625 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 90 KiB Po Szerokość: | Wysokość: | Rozmiar: 109 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 52 KiB Po Szerokość: | Wysokość: | Rozmiar: 88 KiB |
Przed Szerokość: | Wysokość: | Rozmiar: 8.9 KiB Po Szerokość: | Wysokość: | Rozmiar: 15 KiB |
Po Szerokość: | Wysokość: | Rozmiar: 52 KiB |
|
@ -38,15 +38,14 @@ Select a map area or upload a file
|
|||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
At the very beginning the map area to be rendered needs to be determined.
|
||||
For this there are currently four alternatives, available as different
|
||||
For this there are currently three alternatives, available as different
|
||||
form tabs:
|
||||
|
||||
* Directly select a rectangular area on an online map
|
||||
* Use a city or place name to look up its boundaries in the OSM database
|
||||
* Upload a GPX track, the map area will be the tracks bounding box
|
||||
* Upload an Umap file, the map area will be determined by the contained data
|
||||
* Directly select a rectangular area on an online map.
|
||||
* Use a city or place name to look up its boundaries in the OSM database.
|
||||
* Upload a GPX track, Umap export, or GeoJSON file. The map area will be determined by the contained data.
|
||||
|
||||
When uploading a file you can still select a different, e.g. smaller or larger,
|
||||
When uploading files you can still select a different, e.g. smaller or larger,
|
||||
map area afterwards.
|
||||
|
||||
|
||||
|
@ -94,47 +93,49 @@ which no border information is available in OSM yet, or the place area is too la
|
|||
with this web service.
|
||||
|
||||
|
||||
Upload a GPX Track
|
||||
Upload data files
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
image::select-gpx.png[GPX Upload,width=80%,pdfwidth=80%,align=center]
|
||||
image::step-upload.png[File Upload,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
Here you can upload a GPX file containing GPS data and waypoints. The track, and all
|
||||
named waypoints, will then be rendered on top of the base map.
|
||||
Here you can upload data files containing geographic featuers in the form
|
||||
of GPX tracks, http://umap.openstreetmap.fr[Umap] exports, or general
|
||||
GeoJSON files, which will then be rendered on top of the base map.
|
||||
|
||||
For now you can only select a single GPX file, support for multiple track files may
|
||||
be added in the future.
|
||||
The upload form performs some basic checks, so it will complain when one of
|
||||
the uploaded files is not in a supported format, or does not contain any
|
||||
actual data.
|
||||
|
||||
The upload form performs some basic checks, so it will complain when the uploaded
|
||||
file is not in GPX format, or does not contain any usable tracks.
|
||||
|
||||
Note that the file will be stored on the web server, and that the map generated from
|
||||
it (but not the actual GPX file) will be visible to everyone. So do not upload any
|
||||
sensitive data you don't want to to be seen in public, or that you don't have the
|
||||
permission for to share it in public.
|
||||
Note that the files will be stored on the web server, and that the map generated
|
||||
from them (but not the actual uploaded files themselves) will be visible to
|
||||
everyone. So do not upload any sensitive data you don't want to to be seen in
|
||||
public, or that you don't have the permission to share in public.
|
||||
|
||||
Once files have been selected and verified, the form switches back to the
|
||||
area selection tabl, where it will show a preview of the imported file
|
||||
contents, and the optimal map area to display all contained data.
|
||||
|
||||
image::gpx-selected.png[GPX Preview,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
Once a valid GPX file has been selected the form switches back to the area selection
|
||||
tab, and will show a preview of the file contents and the optimal map area to display
|
||||
all contained GPS data.
|
||||
|
||||
If you want to render a different area, e.g. just a smaller part of the track, or a
|
||||
larger area showing more context beyond the track area itself, you can change the
|
||||
If you want to render a different area, e.g. just a smaller part of the data,
|
||||
or a larger area showing more context beyond the data itself, you can change the
|
||||
selection area accordingly.
|
||||
|
||||
Uploading a GPX track
|
||||
+++++++++++++++++++++
|
||||
|
||||
When uploading a GPX track the contained track, and any named way points
|
||||
will be rendered on top of the base map.
|
||||
|
||||
The actual final result of e.g. a rendered GPX track may look like the example below:
|
||||
|
||||
image::gpx-result.png[GPX Render Result,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
The actual final result of a rendered GPX track may look like the example to the left.
|
||||
|
||||
Uploading a Umap File
|
||||
+++++++++++++++++++++
|
||||
|
||||
|
||||
Upload a Umap File
|
||||
^^^^^^^^^^^^^^^^^^
|
||||
|
||||
Here you can upload a file exported from Umap, a service that lets you create online
|
||||
You can upload a file exported from Umap, a service that lets you create online
|
||||
maps with your own markers and drawings on top. We provide you with a way to also use
|
||||
this fine tool to produce customized printed maps with your own data on top, and not
|
||||
only online maps.
|
||||
|
@ -143,20 +144,12 @@ To create an export file from a Umap you created you need to click on the
|
|||
“Embed and Share” Icon on the left side of the Umap interface, and then use
|
||||
“Download Data → Full map data” in the sidebar on the right hand size.
|
||||
|
||||
The upload form performs some basic checks, so it will complain when the uploaded file
|
||||
is not in Umap format, or does not contain any usable data.
|
||||
|
||||
Only data directly added using the Umap drawing tools will be rendered for now.
|
||||
Umap also allows to import external data on the fly, like data form CSV files,
|
||||
or dynamic queries against an Overpass API Server, this kind of data is not
|
||||
supported by this service yet though, and so will not be part of the generated
|
||||
print map.
|
||||
|
||||
Note that the file will be stored on the web server, and that the map generated from it
|
||||
(but not the actual Umap file) will be visible to everyone. So do not upload any
|
||||
sensitive data you don't want to to be seen in public, or that you don't have the
|
||||
permission for to share it in public.
|
||||
|
||||
image::umap-selected.png[Umap Preview,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
Like with GPX uploads, once a valid Umap file has been selected for upload the form
|
||||
|
@ -169,7 +162,7 @@ information, or actually want to show it in a larger map context.
|
|||
image:umap-result.png[Umap Render Result,width=45%,pdfwidth=45%]
|
||||
image:umap-actual.png[Umap Original Online Map,width=45%,pdfwidth=45%]
|
||||
|
||||
An actually rendered Umap map may look like the example on the left hand side,
|
||||
An actually rendered Umap map may look like the example on the left hand side above,
|
||||
while the right hand side shows how the original online Umap looks like.
|
||||
The results are not completely the same , especially when it comes to line stroke
|
||||
width, but this is mostly due to difference in size an resolution of the target
|
||||
|
@ -184,16 +177,21 @@ In this step you can choose between different paper layouts.
|
|||
|
||||
image::step-layout.png[Paper Layout,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
There are three different single page layouts, and one for multi page
|
||||
There are four different single page layouts, and one for multi page
|
||||
booklets.
|
||||
|
||||
The basic single page layout uses the full page for the map.
|
||||
The basic single page layout uses the full page for the map.
|
||||
|
||||
The other two single page layouts add a street index to the
|
||||
The next two single page layouts add a street index to the
|
||||
map, either on the side -- left side for left-to-write languages,
|
||||
or right side for right-to-left languages like Arabic or Hebrew --
|
||||
or at the bottom.
|
||||
|
||||
The fourth single page layout renders a full page map, like the
|
||||
basic layout, and puts the street index on a second page in the
|
||||
generated PDF. The other generated formats will not contain an
|
||||
index as they do not support multi page output.
|
||||
|
||||
The multi page layout creates a multi page booklet with a title
|
||||
page, an overview page, a collection of detail map pages, and
|
||||
a street index.
|
||||
|
@ -242,13 +240,23 @@ Select paper size an orientation
|
|||
|
||||
In this step the minimal required paper size is calculated,
|
||||
and you are given a choice of predefined paper formats that
|
||||
are larger than this, plus a "best fit" option.
|
||||
are larger than this, plus a "best fit" option. You can select
|
||||
one of the suggested paper sizes, or enter a custom width and
|
||||
height that suits your needs yourself.
|
||||
|
||||
image::step-papersize.png[Paper size selection,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
You can also select the paper orientation here. The optimal
|
||||
orientation is pre-selected automatically, but you can override
|
||||
the default choice to fit your own needs.
|
||||
For paper sizes large enough for the selected map area the
|
||||
respective buttons are shown in blue, or in green for the
|
||||
actual selected size. If a paper size is too small for the
|
||||
given area the corresponding button is only shown in gray.
|
||||
|
||||
The left sie of the form will show a rough preview of the
|
||||
chosen size and orientation, showing width and height and
|
||||
a visual representation that will give you an idea of the
|
||||
aspect ration. The preview also contains a rough scale
|
||||
estimate, and the zoom factor the map will be redered with.
|
||||
|
||||
|
||||
Select map title and locale
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -264,9 +272,9 @@ Also a quick summary of your choices is shown.
|
|||
image::step-submit.png[Final steps,width=80%,pdfwidth=80%,align=center]
|
||||
|
||||
The map title is prefilled if you used the city selection
|
||||
to specify the map area. If you uploaded a GPX or Umap
|
||||
file the map title will also be prefilled if title information
|
||||
was found in the uploaded file.
|
||||
to specify the map area. If you uploaded GPX or Umap
|
||||
files the map title will also be prefilled if title information
|
||||
was found in the uploaded files.
|
||||
|
||||
The chosen locale is used for the annotations and copyright
|
||||
information at the bottom of the map, and for section titles
|
||||
|
|
|
@ -37,6 +37,8 @@ from www.settings import RENDERING_RESULT_PATH, RENDERING_RESULT_MAX_SIZE_GB
|
|||
|
||||
import render
|
||||
|
||||
from django import db
|
||||
|
||||
_DEFAULT_POLL_FREQUENCY = 10 # Daemon job polling frequency, in seconds
|
||||
|
||||
_RESULT_MSGS = {
|
||||
|
@ -119,6 +121,11 @@ class MapOSMaticDaemon:
|
|||
"""
|
||||
renderer = self.get_renderer(job, prefix)
|
||||
job.start_rendering()
|
||||
|
||||
# make sure that existing DB connections are not re-used
|
||||
# by the forked subprocess ...
|
||||
db.connections.close_all()
|
||||
|
||||
ret = renderer.run()
|
||||
job.end_rendering(_RESULT_MSGS[ret])
|
||||
return ret == 0
|
||||
|
|
|
@ -479,17 +479,26 @@ class JobRenderer(threading.Thread):
|
|||
LOG.warning("maybe PDF parsing is disabled in the ImageMagic Policy map? (e.g. /etc/ImageMagick-6/policy.xml)")
|
||||
|
||||
elif 'png' in RENDERING_RESULT_FORMATS:
|
||||
try:
|
||||
Image.MAX_IMAGE_PIXELS = None
|
||||
img = Image.open(prefix + '.png')
|
||||
img.save(prefix + '.jpg', quality=50)
|
||||
try:
|
||||
img = img.convert('RGB')
|
||||
img.save(prefix + '.jpg', quality=50)
|
||||
except Exception as e:
|
||||
LOG.warning("PNG to JPEG conversion failed: %s" % e)
|
||||
img.thumbnail((200, 200), Image.ANTIALIAS)
|
||||
img.save(prefix + THUMBNAIL_SUFFIX)
|
||||
try:
|
||||
pngquant_cmd = [ "pngquant", "--output", "%s.8bit.png" % prefix,
|
||||
"%s.png" % prefix ]
|
||||
subprocess.check_call(pngquant_cmd)
|
||||
except Exception as e:
|
||||
LOG.warning("PNG color reduction failed: %s" % e)
|
||||
except Exception as e:
|
||||
LOG.warning("PNG size reduction failed: %s" % e)
|
||||
img.close()
|
||||
|
||||
try:
|
||||
pngquant_cmd = [ "pngquant", "--output", "%s.8bit.png" % prefix,
|
||||
"%s.png" % prefix ]
|
||||
subprocess.check_call(pngquant_cmd)
|
||||
except Exception as e:
|
||||
LOG.warning("PNG color reduction failed: %s" % e)
|
||||
|
||||
def run(self):
|
||||
"""Renders the given job, encapsulating all processing errors and
|
||||
|
@ -532,18 +541,24 @@ class JobRenderer(threading.Thread):
|
|||
if self.job.overlay:
|
||||
for overlay in self.job.overlay.split(","):
|
||||
config.overlays.append(renderer.get_overlay_by_name(overlay))
|
||||
if self.job.track:
|
||||
config.gpx_file = os.path.join(MEDIA_ROOT, self.job.track.name)
|
||||
else:
|
||||
config.gpx_file = False
|
||||
if self.job.umap:
|
||||
config.umap_file = os.path.join(MEDIA_ROOT, self.job.umap.name)
|
||||
else:
|
||||
config.umap_file = False
|
||||
if self.job.poi_file:
|
||||
config.poi_file = os.path.join(MEDIA_ROOT, self.job.poi_file.name)
|
||||
else:
|
||||
config.poi_file = False
|
||||
|
||||
config.import_files = []
|
||||
# legacy files, eventually remove these
|
||||
config.gpx_file = False
|
||||
config.umap_file = False
|
||||
config.poi_file = False
|
||||
|
||||
for file in self.job.uploadfile_set.all():
|
||||
config.import_files.append((file.file_type, os.path.join(MEDIA_ROOT, file.uploaded_file.name)))
|
||||
|
||||
# legacy files, eventually remove these
|
||||
if file.file_type == 'gpx':
|
||||
config.gpx_file = os.path.join(MEDIA_ROOT, file.uploaded_file.name)
|
||||
if file.file_type == 'umap':
|
||||
config.umap_file = os.path.join(MEDIA_ROOT, file.uploaded_file.name)
|
||||
if file.file_type == 'poi':
|
||||
config.poi_file = os.path.join(MEDIA_ROOT, file.uploaded_file.name)
|
||||
|
||||
config.paper_width_mm = self.job.paper_width_mm
|
||||
config.paper_height_mm = self.job.paper_height_mm
|
||||
except KeyboardInterrupt:
|
||||
|
|
|
@ -36,6 +36,8 @@ import ocitysmap
|
|||
from www.maposmatic import models, widgets
|
||||
import www.settings
|
||||
|
||||
from multiupload.fields import MultiFileField
|
||||
|
||||
class MapSearchForm(forms.Form):
|
||||
"""
|
||||
The map search form, allowing search through the rendered maps.
|
||||
|
@ -57,7 +59,7 @@ class MapRenderingJobForm(forms.ModelForm):
|
|||
'maptitle', 'administrative_city',
|
||||
'lat_upper_left', 'lon_upper_left',
|
||||
'lat_bottom_right', 'lon_bottom_right',
|
||||
'track', 'umap', 'submittermail')
|
||||
'submittermail')
|
||||
|
||||
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");'}))
|
||||
|
@ -69,6 +71,7 @@ class MapRenderingJobForm(forms.ModelForm):
|
|||
bbox = widgets.AreaField(label=_("Area"),
|
||||
fields=(forms.FloatField(), forms.FloatField(),
|
||||
forms.FloatField(), forms.FloatField()))
|
||||
uploadfile = MultiFileField(required=False)
|
||||
|
||||
map_lang_flag_list = []
|
||||
for lang_key, lang_name in www.settings.MAP_LANGUAGES_LIST:
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
# Generated by Django 1.11.20 on 2020-02-15 11:20
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
('maposmatic', '0016_submitterip_nullable'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name='UploadFile',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('uploaded_file', models.FileField(blank=True, null=True, upload_to='upload/general/%Y/%m/%d/')),
|
||||
('file_type', models.CharField(choices=[('gpx', 'GPX Track'), ('umap', 'UMAP Export File'), ('poi', 'POI File')], max_length=4)),
|
||||
('job', models.ManyToManyField(to='maposmatic.MapRenderingJob')),
|
||||
],
|
||||
),
|
||||
|
||||
migrations.RunSQL("""
|
||||
INSERT INTO maposmatic_uploadfile (uploaded_file, file_type)
|
||||
SELECT DISTINCT track AS uploaded_file, 'gpx' AS file_type
|
||||
FROM maposmatic_maprenderingjob
|
||||
WHERE track <> ''
|
||||
""",
|
||||
),
|
||||
migrations.RunSQL("""
|
||||
INSERT INTO maposmatic_uploadfile (uploaded_file, file_type)
|
||||
SELECT DISTINCT umap AS uploaded_file, 'umap' AS file_type
|
||||
FROM maposmatic_maprenderingjob
|
||||
WHERE umap <> ''
|
||||
""",
|
||||
),
|
||||
migrations.RunSQL("""
|
||||
INSERT INTO maposmatic_uploadfile (uploaded_file, file_type)
|
||||
SELECT DISTINCT poi_file AS uploaded_file, 'poi' AS file_type
|
||||
FROM maposmatic_maprenderingjob
|
||||
WHERE poi_file <> ''
|
||||
""",
|
||||
),
|
||||
|
||||
migrations.RunSQL("""
|
||||
INSERT INTO maposmatic_uploadfile_job (uploadfile_id, maprenderingjob_id)
|
||||
SELECT file.id AS uploadfile_id, job.id AS maprenderingjob_id
|
||||
FROM maposmatic_uploadfile file
|
||||
JOIN maposmatic_maprenderingjob job
|
||||
ON job.track = file.uploaded_file
|
||||
""",
|
||||
),
|
||||
|
||||
migrations.RemoveField(
|
||||
model_name='maprenderingjob',
|
||||
name='poi_file',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='maprenderingjob',
|
||||
name='track',
|
||||
),
|
||||
migrations.RemoveField(
|
||||
model_name='maprenderingjob',
|
||||
name='umap',
|
||||
),
|
||||
]
|
|
@ -132,12 +132,6 @@ class MapRenderingJob(models.Model):
|
|||
def __str__(self):
|
||||
return self.maptitle.encode('utf-8')
|
||||
|
||||
track = models.FileField(upload_to='upload/tracks/%Y/%m/%d/', null=True, blank=True)
|
||||
|
||||
umap = models.FileField(upload_to='upload/umaps/%Y/%m/%d/', null=True, blank=True)
|
||||
|
||||
poi_file = models.FileField(upload_to='upload/poi/%Y/%m/%d/', null=True, blank=True)
|
||||
|
||||
def maptitle_computized(self):
|
||||
t = self.maptitle.strip()
|
||||
if self.id <= www.settings.LAST_OLD_ID:
|
||||
|
@ -364,3 +358,15 @@ class MapRenderingJob(models.Model):
|
|||
|
||||
if errors:
|
||||
raise ValidationError(errors)
|
||||
|
||||
class UploadFile(models.Model):
|
||||
FILE_TYPES = (
|
||||
('gpx', 'GPX Track'),
|
||||
('umap', 'UMAP Export File'),
|
||||
('poi', 'POI File')
|
||||
);
|
||||
|
||||
uploaded_file = models.FileField(upload_to='upload/general/%Y/%m/%d/', null=True, blank=True)
|
||||
file_type = models.CharField(max_length = 4, choices = FILE_TYPES)
|
||||
|
||||
job = models.ManyToManyField(MapRenderingJob)
|
||||
|
|
|
@ -159,16 +159,13 @@ $('#error-modal').modal('show')
|
|||
|
||||
<ul class="nav nav-tabs" id="locTabs">
|
||||
<li class="nav-item">
|
||||
<a class="nav-link active" id="step-location-bbox-tab" data-toggle="tab" href="#step-location-bbox" role="tab" aria-controls="home" aria-selected="true">{% trans "Geographic area" %}</a>
|
||||
<a class="nav-link active" id="step-location-bbox-tab" data-toggle="tab" href="#step-location-bbox" role="tab" aria-controls="home" aria-selected="true"><i class="fas fa-globe-africa"></i> {% trans "Geographic area" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="step-location-admin-tab" data-toggle="tab" href="#step-location-admin" role="tab" aria-controls="admin" aria-selected="false">{% trans "City search" %}</a>
|
||||
<a class="nav-link" id="step-location-admin-tab" data-toggle="tab" href="#step-location-admin" role="tab" aria-controls="admin" aria-selected="false"><i class="fas fa-search-location"></i> {% trans "City search" %}</a>
|
||||
</li>
|
||||
<li class="nav-item">
|
||||
<a class="nav-link" id="step-location-gpx-tab" data-toggle="tab" href="#step-location-gpx" role="tab" aria-controls="gpx" aria-selected="false">{% trans "GPX track" %}</a>
|
||||
</li>
|
||||
<li calss="nav-item">
|
||||
<a class="nav-link" id="step-location-umap-tab" data-toggle="tab" href="#step-location-umap" role="tab" aria-controls="umap" aria-selected="false">{% trans "Umap data file" %}</a>
|
||||
<a class="nav-link" id="step-location-gpx-tab" data-toggle="tab" href="#step-location-gpx" role="tab" aria-controls="gpx" aria-selected="false"><i class="fas fa-upload"></i> {% trans "File upload" %}</a>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
|
@ -194,52 +191,37 @@ $('#error-modal').modal('show')
|
|||
|
||||
<div class="tab-pane" id="step-location-gpx" role="tabpanel">
|
||||
<div class="col-lg-12">
|
||||
<br/>
|
||||
<fieldset id="track-file">
|
||||
<legend><i class="fas fa-file-upload"></i> {% trans "Upload files" %}</legend>
|
||||
{{ form.uploadfile }}
|
||||
<tt id="file-list">
|
||||
</tt>
|
||||
</fieldset>
|
||||
|
||||
<div style='height: 2ex'></div>
|
||||
|
||||
<div class="alert alert-secondary">
|
||||
{% blocktrans trimmed %}
|
||||
Upload a GPX file here. The GPX track stored in this file,
|
||||
and any named waypoints, will be drawn on top of the created map.
|
||||
Upload GPX, Umap, or general GeoJSON files here.
|
||||
<br/>
|
||||
You can select multiple files at once. If you re-open the
|
||||
file selection dialog to select files, your selection will
|
||||
replace the previous one.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
|
||||
<div style='height: 2ex'></div>
|
||||
|
||||
<div class="alert alert-danger">
|
||||
{% blocktrans trimmed %}
|
||||
<b>Note:</b> The uploaded file will not be made available to anyone, the map created using this
|
||||
<b>Note:</b> The uploaded files will not be made available to anyone, the map created using this
|
||||
file <b>will be publicly visible</b> though.<br/>Do <b>NOT</b> upload any data you don't want to be publicly
|
||||
visible.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<fieldset id="track-file">
|
||||
<legend>{% trans "GPX track file" %}</legend>
|
||||
{{ form.track }}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="tab-pane" id="step-location-umap" role="tabpanel">
|
||||
<div class="col-lg-12">
|
||||
<br/>
|
||||
<div class="alert alert-secondary">
|
||||
{% trans "Upload an <a href='https://umap.openstreetmap.fr/'>Umap</a> file here." %}
|
||||
<p>
|
||||
{% blocktrans trimmed %}
|
||||
Markers, lines and shapes that have been placed on that Umap will be
|
||||
printed on top of the generated map.
|
||||
{% endblocktrans %}
|
||||
<p/>
|
||||
</div>
|
||||
<div class="alert alert-danger">
|
||||
{% blocktrans trimmed %}
|
||||
<b>Note:</b> The uploaded file will not be made available to anyone, the map created using this
|
||||
file <b>will be publicly visible</b> though.<br/>Do <b>NOT</b> upload any data you don't want
|
||||
to be publicly visible.
|
||||
{% endblocktrans %}
|
||||
</div>
|
||||
<fieldset id="umap-file">
|
||||
<legend>{% trans "Umap data file" %}</legend>
|
||||
{{ form.umap }}
|
||||
</fieldset>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="alert alert-info">
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
{% load i18n %}
|
||||
{% load extratags %}
|
||||
|
||||
{% include "./verify-gpx-file.js" %}
|
||||
{% include "./verify-json-file.js" %}
|
||||
|
||||
var file_colors = ['red', 'blue', 'green', 'violet', 'orange'];
|
||||
|
||||
var upload_file_layers = [];
|
||||
var upload_file_bounds = false;
|
||||
|
||||
function clear_upload_layers() {
|
||||
var old_layer;
|
||||
for (old_layer of upload_file_layers) {
|
||||
old_layer.removeFrom(map);
|
||||
}
|
||||
upload_file_layers = [];
|
||||
upload_file_bounds = false;
|
||||
$("#file-list").text('');
|
||||
}
|
||||
|
||||
function add_upload_layer(filename, new_layer) {
|
||||
new_layer.addTo(map);
|
||||
// txt += delim + files[i].name;
|
||||
// delim = '; ';
|
||||
|
||||
upload_file_layers.push(new_layer);
|
||||
|
||||
var new_bbox = new_layer.getBounds();
|
||||
|
||||
if (upload_file_bounds == false) {
|
||||
upload_file_bounds = new_bbox;
|
||||
} else {
|
||||
upload_file_bounds.extend(new_bbox);
|
||||
}
|
||||
|
||||
map.fitBounds(upload_file_bounds);
|
||||
|
||||
if (upload_file_bounds.getSouthWest().equals(upload_file_bounds.getNorthEast())) {
|
||||
upload_file_bounds = map.getBounds();
|
||||
}
|
||||
|
||||
upload_file_bounds = upload_file_bounds.pad(0.1);
|
||||
map.fitBounds(upload_file_bounds);
|
||||
|
||||
// TODO: fill file list display
|
||||
// $("#file-list").text(txt);
|
||||
|
||||
$('#step-location-bbox-tab').tab('show'); // Select geo location tab
|
||||
|
||||
locationFilter.setBounds(upload_file_bounds);
|
||||
locationFilter.enable();
|
||||
}
|
||||
|
||||
function get_layer_titles() {
|
||||
var layer, titles = [];
|
||||
|
||||
for (layer of upload_file_layers) {
|
||||
if (layer.hasOwnProperty('maptitle')) {
|
||||
titles.push(layer.maptitle);
|
||||
}
|
||||
}
|
||||
|
||||
return titles.join("; ");
|
||||
}
|
||||
|
||||
$("#id_uploadfile").change(function() {
|
||||
var upload_file;
|
||||
|
||||
clear_upload_layers();
|
||||
|
||||
var n = 0;
|
||||
for (upload_file of $("#id_uploadfile")[0].files) {
|
||||
var fr, file;
|
||||
|
||||
file = upload_file;
|
||||
fr = new FileReader();
|
||||
fr.filename = file.name;
|
||||
fr.filenum = n;
|
||||
n = n + 1;
|
||||
fr.onload = function (e) {
|
||||
if (e.target.result) {
|
||||
layer = verify_upload_file(file.name, e.target.result, e.target.filenum);
|
||||
if (layer != false) {
|
||||
add_upload_layer(e.target.filename, layer);
|
||||
}
|
||||
} else {
|
||||
alert("Could not read " + e.target.filename);
|
||||
}
|
||||
};
|
||||
fr.readAsText(upload_file);
|
||||
}
|
||||
|
||||
|
||||
});
|
||||
|
||||
function verify_upload_file(filename, data_str, filenum) {
|
||||
if (data_str.startsWith('<?xml')) {
|
||||
return verify_gpx_data(data_str, filename, filenum);
|
||||
} else if (data_str.startsWith('{')) {
|
||||
return verify_json_data(data_str, filename, filenum);
|
||||
}
|
||||
|
||||
alert(filename + ": unknown file type");
|
||||
return false;
|
||||
}
|
|
@ -1,56 +0,0 @@
|
|||
{% load i18n %}
|
||||
{% load extratags %}
|
||||
|
||||
/* handle upload of GPX files*/
|
||||
$("#id_track").change(function() {
|
||||
loadFile($("#id_track")[0], function(xml) {
|
||||
if (/Trident\/|MSIE/.test(window.navigator.userAgent)) {
|
||||
// InterNet Explorer 10 / 11
|
||||
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
||||
xmlDoc.async = false;
|
||||
xmlDoc.loadXML(xml);
|
||||
if (xmlDoc.parseError.errorCode!=0) {
|
||||
alert("not a valid XML file");
|
||||
$("#id_track")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
var parser = new DOMParser();
|
||||
var parsererrorNS = parser.parseFromString('INVALID', 'text/xml').getElementsByTagName("parsererror")[0].namespaceURI;
|
||||
var dom = parser.parseFromString(xml, 'text/xml');
|
||||
if(dom.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0) {
|
||||
alert("not a valid XML file");
|
||||
$("#id_track")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var gpx = new L.GPX(xml, { async: false,
|
||||
marker_options: {
|
||||
wptIconUrls: false,
|
||||
startIconUrl: false,
|
||||
endIconUrl: false,
|
||||
shadowUrl: false,
|
||||
}
|
||||
},
|
||||
).addTo(map);
|
||||
|
||||
var new_bbox = gpx.getBounds();
|
||||
|
||||
if ('_northEast' in new_bbox === false) {
|
||||
alert("Not a GPX file");
|
||||
$("#id_track")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#step-location-bbox').tab('show') // Select geo location tab
|
||||
$('#id_maptitle').val(gpx.get_name());
|
||||
|
||||
new_bbox = new_bbox.pad(0.1)
|
||||
map.fitBounds(new_bbox);
|
||||
locationFilter.setBounds(new_bbox);
|
||||
locationFilter.enable();
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
|
@ -1,103 +0,0 @@
|
|||
{% load i18n %}
|
||||
{% load extratags %}
|
||||
|
||||
// TODO - this shouldn't be hardcoded, but read from the config file instead
|
||||
var umap_style_mapping = {
|
||||
"OpenStreetMap" : "CartoOsm",
|
||||
"OSM-monochrome" : "CartoOsmBw",
|
||||
"OSM Humanitarian (OSM-FR)": "Humanitarian",
|
||||
"OSM-Fr" : "French",
|
||||
"OSM hikebikemap" : "HikeBikeMap",
|
||||
"OSM Deutschland (OSM-DE)" : "GermanCartoOSM",
|
||||
"OSM OpenTopoMap" : "OpenTopoMap",
|
||||
"OSM OpenRiverboatMap" : "OpenRiverboatMap",
|
||||
"OSM Toner (Stamen)" : "Toner"
|
||||
};
|
||||
|
||||
/* handle upload of UMAP files*/
|
||||
$("#id_umap").change(function() {
|
||||
|
||||
loadFile($("#id_umap")[0], function(umap) {
|
||||
var umap_json, layer, feature;
|
||||
var new_features = []
|
||||
|
||||
try {
|
||||
umap_json = JSON.parse(umap);
|
||||
} catch(e) {
|
||||
alert('This does not look like a valid GeoJson or Umap export file (json parse error)');
|
||||
$("#id_umap")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
|
||||
if (umap_json.type == 'umap') {
|
||||
for (layer in umap_json.layers) {
|
||||
for (feature in umap_json.layers[layer].features) {
|
||||
new_features.push(umap_json.layers[layer].features[feature]);
|
||||
}
|
||||
}
|
||||
|
||||
var new_geojson = {'type': 'FeatureCollection', 'features': new_features};
|
||||
|
||||
var json_layer = L.geoJson(new_geojson).addTo(map);
|
||||
var new_bbox = json_layer.getBounds();
|
||||
|
||||
if ('_northEast' in new_bbox === false) {
|
||||
alert('Umap file contains no geometry data');
|
||||
$("#id_umap")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#step-location-bbox').tab('show') // Select geo location tab
|
||||
$('#id_maptitle').val(umap_json.properties.name);
|
||||
|
||||
var umap_title;
|
||||
try {
|
||||
umap_title = umap_json.properties.tilelayer.name;
|
||||
} catch (err) {
|
||||
umap_title = "OSM-Fr";
|
||||
}
|
||||
if (umap_title in umap_style_mapping) {
|
||||
$("input:radio[name=stylesheet][value='"+umap_style_mapping[umap_title]+"']").prop("checked",true);
|
||||
}
|
||||
|
||||
map.fitBounds(new_bbox);
|
||||
|
||||
if (new_bbox.getSouthWest().equals(new_bbox.getNorthEast())) {
|
||||
new_bbox = map.getBounds();
|
||||
}
|
||||
|
||||
new_bbox = new_bbox.pad(0.1);
|
||||
locationFilter.setBounds(new_bbox);
|
||||
locationFilter.enable();
|
||||
} else if (umap_json.type == 'FeatureCollection') {
|
||||
var json_layer = L.geoJson(umap_json).addTo(map);
|
||||
var new_bbox = json_layer.getBounds();
|
||||
|
||||
if ('_northEast' in new_bbox === false) {
|
||||
alert('GeoJson file contains no geometry data');
|
||||
$("#id_umap")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#step-location-bbox').tab('show') // Select geo location tab
|
||||
|
||||
map.fitBounds(new_bbox);
|
||||
|
||||
if (new_bbox.getSouthWest().equals(new_bbox.getNorthEast())) {
|
||||
new_bbox = map.getBounds();
|
||||
}
|
||||
|
||||
new_bbox = new_bbox.pad(0.1);
|
||||
locationFilter.setBounds(new_bbox);
|
||||
locationFilter.enable();
|
||||
return true;
|
||||
} else {
|
||||
alert('This does not look like a valid GeoJson or Umap export file (wrong or missing type info)');
|
||||
$("#id_umap")[0].value = '';
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
});
|
||||
});
|
|
@ -0,0 +1,53 @@
|
|||
{% load i18n %}
|
||||
{% load extratags %}
|
||||
|
||||
function verify_gpx_data(data_str, filename, filenum)
|
||||
{
|
||||
if (/Trident\/|MSIE/.test(window.navigator.userAgent)) {
|
||||
// InterNet Explorer 10 / 11
|
||||
xmlDoc = new ActiveXObject("Microsoft.XMLDOM");
|
||||
xmlDoc.async = false;
|
||||
xmlDoc.loadXML(data_str);
|
||||
if (xmlDoc.parseError.errorCode!=0) {
|
||||
alert("not a valid XML file");
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
var parser = new DOMParser();
|
||||
var parsererrorNS = parser.parseFromString('INVALID', 'text/xml').getElementsByTagName("parsererror")[0].namespaceURI;
|
||||
var dom = parser.parseFromString(data_str, 'text/xml');
|
||||
if(dom.getElementsByTagNameNS(parsererrorNS, 'parsererror').length > 0) {
|
||||
alert(filename + "is not a valid XML file");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
var color = file_colors[filenum % file_colors.length];
|
||||
var gpx_layer = new L.GPX(data_str, { async: false,
|
||||
polyline_options: {
|
||||
color: color,
|
||||
opacity: 0.75,
|
||||
},
|
||||
marker_options: {
|
||||
wptIconUrls: false,
|
||||
startIconUrl: false,
|
||||
endIconUrl: false,
|
||||
shadowUrl: false,
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
var new_bbox = gpx_layer.getBounds();
|
||||
|
||||
if ('_northEast' in new_bbox === false) {
|
||||
alert(filename + "is not a valid GPX file");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (gpx_layer.get_name() != '') {
|
||||
gpx_layer.maptitle = gpx_layer.get_name();
|
||||
}
|
||||
|
||||
return gpx_layer;
|
||||
}
|
||||
|
|
@ -0,0 +1,97 @@
|
|||
{% load i18n %}
|
||||
{% load extratags %}
|
||||
|
||||
function verify_json_data(data_str, filename, filenum)
|
||||
{
|
||||
var json_data;
|
||||
|
||||
try {
|
||||
json_data = JSON.parse(data_str);
|
||||
} catch(e) {
|
||||
alert(filename + 'does not look like a valid GeoJson or Umap export file (json parse error)');
|
||||
return false;
|
||||
}
|
||||
|
||||
if (json_data.type == 'umap') {
|
||||
return verify_umap_json_data(json_data, filename, filenum);
|
||||
} else if (json_data.type == 'FeatureCollection') {
|
||||
return verify_geojson_data(json_data, filename, filenum);
|
||||
}
|
||||
|
||||
alert(filename + ' does not look like a valid GeoJson or Umap export file (wrong or missing type info)');
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function verify_geojson_data(json_data, filename, filenum)
|
||||
{
|
||||
var color = file_colors[filenum % file_colors.length];
|
||||
var json_layer = L.geoJson(json_data, {
|
||||
style: function(feature) {
|
||||
return { color: color };
|
||||
}});
|
||||
|
||||
var new_bbox = json_layer.getBounds();
|
||||
|
||||
if ('_northEast' in new_bbox === false) {
|
||||
alert(filename + ' does not seem to contain geometry data');
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#step-location-bbox').tab('show') // Select geo location tab
|
||||
|
||||
return json_layer;
|
||||
}
|
||||
|
||||
// TODO - this shouldn't be hardcoded, but read from the config file instead
|
||||
var umap_style_mapping = {
|
||||
"OpenStreetMap" : "CartoOsm",
|
||||
"OSM-monochrome" : "CartoOsmBw",
|
||||
"OSM Humanitarian (OSM-FR)": "Humanitarian",
|
||||
"OSM-Fr" : "French",
|
||||
"OSM hikebikemap" : "HikeBikeMap",
|
||||
"OSM Deutschland (OSM-DE)" : "GermanCartoOSM",
|
||||
"OSM OpenTopoMap" : "OpenTopoMap",
|
||||
"OSM OpenRiverboatMap" : "OpenRiverboatMap",
|
||||
"OSM Toner (Stamen)" : "Toner"
|
||||
};
|
||||
|
||||
function verify_umap_json_data(json_data, filename, filenum)
|
||||
{
|
||||
var layer, feature, new_features = [];
|
||||
|
||||
for (layer in json_data.layers) {
|
||||
for (feature in json_data.layers[layer].features) {
|
||||
new_features.push(json_data.layers[layer].features[feature]);
|
||||
}
|
||||
}
|
||||
|
||||
var new_geojson = {'type': 'FeatureCollection', 'features': new_features};
|
||||
|
||||
var color = file_colors[filenum % file_colors.length];
|
||||
var json_layer = L.geoJson(new_geojson, {
|
||||
style: function(feature) {
|
||||
return { color: color };
|
||||
}});
|
||||
var new_bbox = json_layer.getBounds();
|
||||
|
||||
if ('_northEast' in new_bbox === false) {
|
||||
alert(filename + ' does not seem to contain geometry data');
|
||||
return false;
|
||||
}
|
||||
|
||||
$('#step-location-bbox').tab('show') // Select geo location tab
|
||||
json_layer.maptitle = json_data.properties.name;
|
||||
|
||||
try {
|
||||
var umap_style = json_data.properties.tilelayer.name;
|
||||
if (umap_style in umap_style_mapping) {
|
||||
// TODO: add a proper function for selecting the map style
|
||||
$("input:radio[name=stylesheet][value='" + umap_style_mapping[umap_style] + "']").prop("checked", true);
|
||||
}
|
||||
} catch {
|
||||
// ignore
|
||||
}
|
||||
|
||||
return json_layer;
|
||||
}
|
|
@ -82,38 +82,7 @@ function setPrevNextLinks() {
|
|||
|
||||
{% include "./wizard-parts/wizardmap.js" %}
|
||||
|
||||
/* general file upload event handler */
|
||||
function loadFile(input, onload_func) {
|
||||
var file, fr;
|
||||
if (typeof window.FileReader !== 'function') {
|
||||
console.log("The file API isn't supported on this browser yet.");
|
||||
return;
|
||||
}
|
||||
if (!input) {
|
||||
console.log("Um, couldn't find the fileinput element.");
|
||||
}
|
||||
else if (!input.files) {
|
||||
console.log("This browser doesn't seem to support the `files` property of file inputs.");
|
||||
}
|
||||
else if (!input.files[0]) {
|
||||
console.log("Please select a file before clicking 'Load'");
|
||||
}
|
||||
else {
|
||||
file = input.files[0];
|
||||
fr = new FileReader();
|
||||
fr.onload = receivedText;
|
||||
fr.readAsText(file);
|
||||
}
|
||||
function receivedText() {
|
||||
onload_func(fr.result);
|
||||
}
|
||||
}
|
||||
|
||||
{% include "./wizard-parts/upload-gpx-file.js" %}
|
||||
{% include "./wizard-parts/upload-umap-file.js" %}
|
||||
|
||||
|
||||
|
||||
{% include "./wizard-parts/upload-files.js" %}
|
||||
|
||||
var currentTab = 0; // Current tab is set to be the first tab (0)
|
||||
showTab(currentTab); // Display the current tab
|
||||
|
@ -236,7 +205,10 @@ function country_lang(country_code)
|
|||
|
||||
function prepareLangTitle() {
|
||||
// Prepare the language list
|
||||
country_lang(country);
|
||||
country_lang(country);
|
||||
|
||||
// Set title text
|
||||
$('#id_maptitle').val(get_layer_titles());
|
||||
|
||||
// Seed the summary fields
|
||||
if ($('#id_administrative_osmid').val()) {
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
import datetime
|
||||
import logging
|
||||
import json
|
||||
import os
|
||||
|
||||
from django.core.paginator import Paginator, InvalidPage, EmptyPage
|
||||
from django.core.urlresolvers import reverse
|
||||
|
@ -125,6 +126,18 @@ def donate_thanks(request):
|
|||
"""The thanks for donation page."""
|
||||
return render_to_response('maposmatic/donate-thanks.html')
|
||||
|
||||
def create_upload_file(job, file):
|
||||
first_line = file.readline()
|
||||
f = file.open()
|
||||
LOG.info("firstline type %s" % type(first_line))
|
||||
if first_line.startswith(b'<?xml'):
|
||||
file_type = 'gpx'
|
||||
else:
|
||||
file_type = 'umap'
|
||||
file_instance = models.UploadFile(uploaded_file = file, file_type = file_type)
|
||||
file_instance.save()
|
||||
file_instance.job.add(job)
|
||||
|
||||
def new(request):
|
||||
"""The map creation page and form."""
|
||||
|
||||
|
@ -157,8 +170,13 @@ def new(request):
|
|||
job.index_queue_at_submission = (models.MapRenderingJob.objects
|
||||
.queue_size())
|
||||
job.nonce = helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
|
||||
|
||||
job.save()
|
||||
|
||||
files = request.FILES.getlist('uploadfile')
|
||||
for file in files:
|
||||
create_upload_file(job, file)
|
||||
|
||||
return HttpResponseRedirect(reverse('map-by-id-and-nonce',
|
||||
args=[job.id, job.nonce]))
|
||||
else:
|
||||
|
@ -306,12 +324,11 @@ def recreate(request):
|
|||
.queue_size())
|
||||
newjob.nonce = helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
|
||||
|
||||
newjob.track = job.track
|
||||
newjob.umap = job.umap
|
||||
newjob.poi_file = job.poi_file
|
||||
|
||||
newjob.save()
|
||||
|
||||
for each in job.uploadfile_set.all():
|
||||
each.job.add(newjob)
|
||||
|
||||
return HttpResponseRedirect(reverse('map-by-id-and-nonce',
|
||||
args=[newjob.id, newjob.nonce]))
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ from django.utils.translation import ugettext_lazy as _
|
|||
from .settings_local import *
|
||||
try:
|
||||
from .settings_bounds import *
|
||||
except:
|
||||
except ModuleNotFoundError:
|
||||
pass
|
||||
|
||||
from . import logconfig
|
||||
|
|
|
@ -4,6 +4,11 @@
|
|||
"lockfileVersion": 1,
|
||||
"requires": true,
|
||||
"dependencies": {
|
||||
"@fortawesome/fontawesome-free": {
|
||||
"version": "5.12.0",
|
||||
"resolved": "https://registry.npmjs.org/@fortawesome/fontawesome-free/-/fontawesome-free-5.12.0.tgz",
|
||||
"integrity": "sha512-vKDJUuE2GAdBERaQWmmtsciAMzjwNrROXA5KTGSZvayAsmuTGjam5z6QNqNPCwDfVljLWuov1nEC3mEQf/n6fQ=="
|
||||
},
|
||||
"@mapbox/leaflet-omnivore": {
|
||||
"version": "0.3.4",
|
||||
"resolved": "https://registry.npmjs.org/@mapbox/leaflet-omnivore/-/leaflet-omnivore-0.3.4.tgz",
|
||||
|
|