Merge branch 'site-osm-baustelle' of https://github.com/hholzgra/maposmatic into site-osm-baustelle

site-osm-baustelle
Hartmut Holzgraefe 2022-10-09 12:52:01 +02:00
commit 6d4fcacdae
137 zmienionych plików z 725 dodań i 199 usunięć

Wyświetl plik

@ -33,7 +33,7 @@ pdf: api-docs.pdf user-guide.pdf README.pdf INSTALL.pdf
clean:
rm -f *.html *.pdf *~
install: all
install: html
cp *.html ../www/maposmatic/templates/generated
.PHONY = html clean all install

Wyświetl plik

@ -199,7 +199,7 @@ Expects render job named parameters as a JSON collection.
Possible parameters:
osmid:: ID of an object in the osm2pgsql
osmid:: ID of an object in the osm2pgsql polygon table
bbox_top::
bbox_bottom::
bbox_left::
@ -317,7 +317,7 @@ Example results
----
HTTP/1.1 202 Accepted
[...]
Location: /api/jobs/36
Location: /api/jobs/214
Content-Type: text/json
{
@ -325,15 +325,16 @@ Content-Type: text/json
"bbox_left": 8.52,
"bbox_right": 8.50,
"bbox_top": 52.02,
"id": 36,
"id": 214,
"interactive": "https://api.get-map.org/maps/214"
"language": "en_US.UTF-8",
"layout": "plain",
"nonce": "ESubNGmuwYGxPEGM",
"paper_height_mm": 297,
"paper_width_mm": 210,
"status": 0,
"styles": "CartoOSM"
"title": "API Test",
"styles": "CartoOSM",
"title": "API Test"
}
----
@ -404,6 +405,35 @@ Content-type: application/json
----
Cancel a submitted job
~~~~~~~~~~~~~~~~~~~~~~
Request URL
^^^^^^^^^^^
----
POST https://api.get-map.org/apis/cancel_job
----
Expects id and nonce of job to cancel as a JSON collection.
Nonce is only returned when creating a job, so only the creator
should be able to cancel a job but nobody else.
Required parameters:
id:: ID of job to cancel
nonce:: nonce string as returned on job creation call
Return codes
^^^^^^^^^^^^
204:: 'No content' when job was cancelled successfully, or already no longer in a wait queue
400:: 'Bad request' when either id or nonce parameter are missing
403:: 'Forbidden' when the given nonce does not match the jobs actual nonce
404:: 'Not found' when no job with the given id exists
Example code
------------

Wyświetl plik

@ -63,8 +63,9 @@ class MapOSMaticDaemon:
stalls the queue.
"""
def __init__(self, frequency=_DEFAULT_POLL_FREQUENCY):
def __init__(self, frequency=_DEFAULT_POLL_FREQUENCY, queue_name="default"):
self.frequency = frequency
self.queue_name = queue_name
LOG.info("MapOSMatic rendering daemon started.")
self.rollback_orphaned_jobs()
@ -89,7 +90,7 @@ class MapOSMaticDaemon:
while True:
try:
job = MapRenderingJob.objects.to_render()[0]
job = MapRenderingJob.objects.to_render(self.queue_name)[0]
self.dispatch(job)
# check disk space after rendering
@ -144,8 +145,8 @@ class MapOSMaticDaemon:
class ForkingMapOSMaticDaemon(MapOSMaticDaemon):
def __init__(self, frequency=_DEFAULT_POLL_FREQUENCY):
MapOSMaticDaemon.__init__(self, frequency)
def __init__(self, frequency=_DEFAULT_POLL_FREQUENCY, queue_name="default"):
MapOSMaticDaemon.__init__(self, frequency, queue_name)
LOG.info('This is the forking daemon. Will fork to process each job.')
def get_renderer(self, job, prefix):
@ -273,7 +274,10 @@ if __name__ == '__main__':
sys.exit(1)
try:
daemon = ForkingMapOSMaticDaemon()
if len(sys.argv) == 2:
daemon = ForkingMapOSMaticDaemon(queue_name=sys.argv[1])
else:
daemon = ForkingMapOSMaticDaemon()
daemon.serve()
except Exception as e:
LOG.exception('Fatal error during daemon execution!')

Wyświetl plik

@ -114,7 +114,7 @@ class ThreadingJobRenderer:
'timeout': self.__timeout / 60
}
msg = template.render(context)
mailer.sendmail(DAEMON_ERRORS_EMAIL_FROM,
[admin[1] for admin in ADMINS], msg)
LOG.info("Email notification sent.")
@ -196,8 +196,8 @@ class ForkingJobRenderer:
'title': self.__job.maptitle,
'timeout': self.__timeout / 60
}
msg = template.render(context)
msg = template.render(context)
mailer.sendmail(DAEMON_ERRORS_EMAIL_FROM,
[admin[1] for admin in ADMINS], msg)
LOG.info("Email notification sent.")
@ -309,7 +309,7 @@ class JobRenderer(threading.Thread):
'title': self.job.maptitle
}
msg = template.render(context)
mailer.sendmail(DAEMON_ERRORS_EMAIL_FROM, self.job.submittermail, msg.encode("utf8"))
LOG.info("Email notification sent.")
except Exception as e:
@ -359,7 +359,7 @@ class JobRenderer(threading.Thread):
'tb': ''.join(traceback.format_exception(*exc_info))
}
msg = template.render(context)
mailer.sendmail(DAEMON_ERRORS_EMAIL_FROM,
[admin[1] for admin in ADMINS], msg)
LOG.info("Error report sent.")
@ -440,6 +440,7 @@ class JobRenderer(threading.Thread):
# TODO have the create form provide this
config.origin_url = 'https://print.get-map.org' + self.job.get_absolute_url()
config.title = self.job.maptitle
config.osmid = self.job.administrative_osmid
@ -456,6 +457,9 @@ class JobRenderer(threading.Thread):
self.job.lon_bottom_right)
config.language = self.job.map_language
config.indexer = self.job.indexer
config.stylesheet = renderer.get_stylesheet_by_name(
self.job.stylesheet)
config.overlays = []

Wyświetl plik

@ -0,0 +1,18 @@
Copy both .service files to /etc/systemd/system and edit them to change the
path to the wrapper.py script to the actual script location on your system.
The maposmatic-render.service takes render requests from the "default" queue.
You can start this service by simply invoking
systemctl start maposmatic-render
and enable it permanently using
systemctl enable maposmatic-render
Jobs from additional queues, like e.g. "api", can be rendered using the
maposmatic-render@.service template. To start a service for a specific
queue add the queue name after the @ sign, e.g.:
systemctl start maposmatic-render@api

Wyświetl plik

@ -0,0 +1,14 @@
[Unit]
Description=Maposmatic render daemon processing queue: %I
After=multi-user.target
Wants=postgresql.service
[Service]
Type=idle
User=maposmatic
ExecStart=/usr/bin/python /path/to/maposmatic/scripts/wrapper.py scripts/daemon.py %i
[Install]
WantedBy=multi-user.target

Wyświetl plik

@ -25,7 +25,7 @@ import json
from django.core.exceptions import ValidationError
from django.core.files import File
from django.core.files.base import ContentFile
from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponseNotFound, HttpResponse, HttpResponseNotAllowed, Http404
from django.http import HttpResponseRedirect, HttpResponseBadRequest, HttpResponseNotFound, HttpResponse, HttpResponseNotAllowed, HttpResponseForbidden, Http404
from django.forms.models import model_to_dict
from django.shortcuts import get_object_or_404
from django.db import connections
@ -90,9 +90,7 @@ def layouts(request):
return HttpResponse( content=json.dumps(result, indent=4, sort_keys=True, default=str), content_type='text/json')
def job_stati(request):
def job_stati_dict(id = None):
# TODO do not hard-code these, get them from OCitysMap cleanly
result = {
"0": "Submitted",
@ -102,16 +100,40 @@ def job_stati(request):
"4": "Cancelled"
}
return HttpResponse( content=json.dumps(result, indent=4, sort_keys=True, default=str), content_type='text/json')
if id is not None:
return result[str(id)]
def heatdata(request):
return result
def job_stati(request):
return HttpResponse( content=json.dumps(job_stati_dict(), indent=4, sort_keys=True, default=str), content_type='text/json')
def heatdata(request, days=1):
query = """
select round(lat::numeric, 4)::float as lat
, round(lng::numeric, 4)::float as lng
, count(*) as count
from (
select (lat_upper_left + lat_bottom_right)/2 as lat
, (lon_upper_left + lon_bottom_right)/2 as lng
, 1 as count
from maposmatic_maprenderingjob
where lat_upper_left is not null
"""
and submission_time BETWEEN LOCALTIMESTAMP - INTERVAL '%s days' AND LOCALTIMESTAMP
union
select (north + south)/2 as lat
, (west + east)/2 as lng
from maposmatic_maprenderingjob m
left outer join dblink('dbname=gis', 'SELECT osm_id, west, east, north, south FROM place') AS p(osm_id bigint, west float, east float, north float, south float)
on -m.administrative_osmid = p.osm_id
where m.administrative_osmid is not null
and submission_time BETWEEN LOCALTIMESTAMP - INTERVAL '%s days' AND LOCALTIMESTAMP
) x group by lat, lng;
;
""" % (days, days)
data = { "max": 8, "data": [] }
@ -129,7 +151,7 @@ select (lat_upper_left + lat_bottom_right)/2 as lat
cursor.close()
return HttpResponse(content="var data = " + json.dumps(data),
return HttpResponse(content="var data = " + json.dumps(data, indent=2),
content_type='application/javascript')
except Exception as e:
@ -155,8 +177,9 @@ def _jobs_get(request, job_id):
reply = model_to_dict(job)
result = {}
result['id'] = job_id
result['id'] = int(job_id)
result['status'] = reply['status']
result['status_msg'] = job_stati_dict(reply['status'])
if reply['administrative_osmid']:
result['osm_id'] = reply['administrative_osmid']
@ -188,6 +211,8 @@ def _jobs_get(request, job_id):
for key, val in files['maps'].items():
result['files'][key] = request.build_absolute_uri(val[0])
result['interactive'] = request.build_absolute_uri('../../../maps/%d' % reply['id'])
if job.status <= 1:
status = 202
else:
@ -209,10 +234,15 @@ def _jobs_post(request):
input = json.loads(request.POST['job'])
valid_keys = ['osmid',
'bbox',
'bbox_bottom',
'bbox_left',
'bbox_right',
'bbox_top',
'min_lat',
'max_lat',
'min_lon',
'max_lon',
'import_urls',
'language',
'layout',
@ -233,6 +263,16 @@ def _jobs_post(request):
if 'osmid' in input:
job.administrative_osmid= input['osmid']
elif 'bbox' in input:
job.lat_upper_left = input['bbox'][0]
job.lon_upper_left = input['bbox'][3]
job.lat_bottom_right = input['bbox'][2]
job.lon_bottom_right = input['bbox'][1]
elif 'lat_min' in input and 'lat_max' in input and 'lon_min' in input and 'lon_max' in input:
job.lat_upper_left = input['max_lat']
job.lon_upper_left = input['min_lon']
job.lat_bottom_right = input['min_lat']
job.lon_bottom_right = input['max_lon']
elif 'bbox_top' in input and 'bbox_bottom' in input and 'bbox_left' in input and 'bbox_right' in input:
job.lat_upper_left = input['bbox_top']
job.lon_upper_left = input['bbox_left']
@ -370,6 +410,8 @@ def _jobs_post(request):
if not job.layout:
job.layout = 'plain'
job.queue = 'api'
if not result['error']:
job.status = 0
if www.settings.SUBMITTER_IP_LIFETIME != 0:
@ -377,7 +419,7 @@ def _jobs_post(request):
else:
job.submitterip = None
job.index_queue_at_submission = (models.MapRenderingJob.objects.queue_size())
job.index_queue_at_submission = (models.MapRenderingJob.objects.queue_size(job.queue))
job.nonce = helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
try:
job.full_clean()
@ -393,7 +435,15 @@ def _jobs_post(request):
reply = model_to_dict(job)
result['id'] = reply['id']
result['nonce'] = reply['nonce']
result['status'] = reply['status']
result['status_msg'] = job_stati_dict(reply['status'])
if reply['status'] == 0:
result['queue_size'] = models.MapRenderingJob.objects.queue_size()
else:
result['queue_size'] = 0
result['files'] = {}
result['interactive'] = request.build_absolute_uri('../../maps/%d' % reply['id'])
@ -588,3 +638,29 @@ def _process_poi_file(file):
raise RuntimeError('Cannot parse POI file: %s' % e)
return result
def cancel_job(request):
"""API handler for canceling rendering requests"""
if request.method != 'POST':
return HttpResponseNotAllowed(['POST'])
if request.content_type == 'application/json':
input = json.loads(request.body.decode('utf-8-sig'))
else:
input = json.loads(request.POST['job'])
if not "id" in input or not "nonce" in input:
return HttpResponseBadRequest()
job = get_object_or_404(models.MapRenderingJob, id = input['id'])
reply = model_to_dict(job)
if input['nonce'] != reply['nonce']:
return HttpResponseForbidden()
if job.is_waiting():
job.cancel()
return HttpResponse(status=204)

Wyświetl plik

@ -32,7 +32,7 @@ import datetime
from .models import MapRenderingJob
import www.settings
from www.maposmatic import forms
from www.maposmatic import forms, models
import logging
@ -98,6 +98,51 @@ def get_waymarked_database_last_update():
return None
def queue_states():
queues = {}
for queue_name in www.settings.QUEUE_NAMES:
status = {}
status['running'] = www.settings.is_daemon_running(queue_name)
status['size'] = models.MapRenderingJob.objects.queue_size(queue_name)
queues[queue_name] = status
return queues
def all_queues_running():
for queue_name in www.settings.QUEUE_NAMES:
if not www.settings.is_daemon_running(queue_name):
return False
return True
def queues_overall_state():
queues_total = 0
queues_running = 0
for queue_name in www.settings.QUEUE_NAMES:
queues_total = queues_total + 1
if www.settings.is_daemon_running(queue_name):
queues_running = queues_running + 1
if queues_running == 0:
return "danger"
if queues_running < queues_total:
return "warning"
return "success"
def queues_overall_symbol():
queues_total = 0
queues_running = 0
for queue_name in www.settings.QUEUE_NAMES:
queues_total = queues_total + 1
if www.settings.is_daemon_running(queue_name):
queues_running = queues_running + 1
if queues_running == 0:
return "times"
if queues_running < queues_total:
return "exclamation"
return "check"
def all(request):
# Do not add the useless overhead of parsing blog entries when generating
# the rss feed
@ -112,7 +157,7 @@ def all(request):
paypal_lang_code = "en_US"
paypal_country_code = "US"
daemon_running = www.settings.is_daemon_running()
daemon_running = all_queues_running()
gis_lastupdate = get_osm_database_last_update()
gis_lag_ok = (gis_lastupdate
@ -168,4 +213,8 @@ def all(request):
'SUBMITTER_MAIL_LIFETIME': www.settings.SUBMITTER_MAIL_LIFETIME,
'SUBMITTER_IP_LIFETIME': www.settings.SUBMITTER_IP_LIFETIME,
'queue_states': queue_states(),
'queues_overall_state': queues_overall_state(),
'queues_overall_symbol': queues_overall_symbol(),
}

Wyświetl plik

@ -55,14 +55,15 @@ class MapRenderingJobForm(forms.ModelForm):
"""
class Meta:
model = models.MapRenderingJob
fields = ('layout', 'stylesheet', 'overlay',
fields = ('layout', 'indexer', 'stylesheet', 'overlay',
'maptitle', 'administrative_city',
'lat_upper_left', 'lon_upper_left',
'lat_bottom_right', 'lon_bottom_right',
'submittermail')
mode = forms.CharField(initial='bbox', widget=forms.HiddenInput)
layout = forms.ChoiceField(choices=(), widget=forms.RadioSelect(attrs= { 'onchange' : 'clearPaperSize(this.value); $("#layout-preview").attr("src","/media/img/layout/"+this.value+".png");'}))
layout = forms.ChoiceField(choices=(), widget=forms.Select(attrs= { 'onchange' : 'clearPaperSize(this.value); load_preview("#layout-preview", this.value);'}))
indexer = forms.ChoiceField(choices=(), widget=forms.Select())
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)
paper_width_mm = forms.IntegerField(widget=forms.NumberInput(attrs= {'onchange' : 'change_papersize();', 'style': 'width: 5em;'}), min_value=100, max_value=2000)
@ -96,32 +97,24 @@ class MapRenderingJobForm(forms.ModelForm):
self._ocitysmap = ocitysmap.OCitySMap(www.settings.OCITYSMAP_CFG_PATH)
layout_renderers = self._ocitysmap.get_all_renderers()
indexers = self._ocitysmap.get_all_indexers()
stylesheets = self._ocitysmap.get_all_style_configurations()
overlays = self._ocitysmap.get_all_overlay_configurations()
self.fields['layout'].choices = []
# TODO move descriptions to ocitysmap side
for r in layout_renderers:
if r.name == 'plain':
description = _(u"Full-page layout without street index")
elif r.name == 'single_page_index_side':
description = _(u"Full-page layout with the street index on the side")
elif r.name == 'single_page_index_bottom':
description = _(u"Full-page layout with the street index at the bottom")
elif r.name == 'single_page_index_extra_page':
description = _(u"Full-page layout with the street index on extra page (PDF only)")
elif r.name == 'tk25':
description = _(u"TK25 full page layout")
elif r.name == 'multi_page':
description = _(u"Multi-page layout")
else:
continue
# description = mark_safe(_(u"The %(layout_name)s layout") % {'layout_name':r.name})
self.fields['layout'].choices.append((r.name, description))
self.fields['layout'].choices.append((r.name, r.description))
if not self.fields['layout'].initial:
self.fields['layout'].initial = layout_renderers[0].name
# TODO get these from ocitysmap instead of hardcoding here
self.fields['indexer'].choices = self._ocitysmap.get_all_indexers_name_desc()
if not self.fields['indexer'].initial:
self.fields['indexer'].initial = 'Street' # TODO: make configurable
style_choices = {"": []}
for s in stylesheets:
if s.description is not None:
@ -136,7 +129,7 @@ class MapRenderingJobForm(forms.ModelForm):
group = s.group
else:
group = ""
if group not in style_choices:
style_choices[group] = []
style_choices[s.group].append((s.name, description))
@ -185,6 +178,7 @@ class MapRenderingJobForm(forms.ModelForm):
city = cleaned_data.get("administrative_city")
title = cleaned_data.get("maptitle")
layout = cleaned_data.get("layout")
indexer = cleaned_data.get("indexer")
stylesheet = cleaned_data.get("stylesheet")
overlay_array = []
try:
@ -199,6 +193,11 @@ class MapRenderingJobForm(forms.ModelForm):
self._errors["layout"] = ErrorList([msg])
del cleaned_data["layout"]
if indexer == '':
msg = _(u"Indexer required")
self._errors["indexer"] = ErrorList([msg])
del cleaned_data["indexer"]
if stylesheet == '':
msg = _(u"Stylesheet required")
self._errors["stylesheet"] = ErrorList([msg])
@ -293,6 +292,7 @@ class MapPaperSizeForm(forms.Form):
"""
osmid = forms.IntegerField(required=False)
layout = forms.CharField(max_length=256)
indexer = forms.CharField(max_length=256)
stylesheet = forms.CharField(max_length=256)
lat_upper_left = forms.FloatField(required=False, min_value=-90.0, max_value=90.0)
lon_upper_left = forms.FloatField(required=False, min_value=-180.0, max_value=180.0)

Wyświetl plik

@ -0,0 +1,18 @@
# Generated by Django 2.2.12 on 2022-08-16 12:40
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maposmatic', '0023_no_NULL_maptitles'),
]
operations = [
migrations.AddField(
model_name='maprenderingjob',
name='queue',
field=models.CharField(default='default', max_length=40),
),
]

Wyświetl plik

@ -0,0 +1,19 @@
# Generated by Django 2.2.12 on 2022-08-21 15:45
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maposmatic', '0024_maprenderingjob_queue'),
]
operations = [
migrations.AddField(
model_name='maprenderingjob',
name='indexer',
field=models.CharField(max_length=256, null=True),
),
migrations.RunSQL("UPDATE maposmatic_maprenderingjob SET indexer = 'Street' WHERE layout LIKE '%index%'"),
]

Wyświetl plik

@ -0,0 +1,11 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maposmatic', '0025_maprenderingjob_indexer'),
]
operations = [
migrations.RunSQL("UPDATE maposmatic_maprenderingjob j SET indexer = 'poi' FROM maposmatic_uploadfile_job uj, maposmatic_uploadfile u WHERE j.id = uj.maprenderingjob_id AND uj.uploadfile_id = u.id AND u.file_type = 'poi'"),
]

Wyświetl plik

@ -0,0 +1,12 @@
from django.db import migrations, models
class Migration(migrations.Migration):
dependencies = [
('maposmatic', '0026_maprenderingjob_indexer'),
]
operations = [
migrations.RunSQL("UPDATE maposmatic_maprenderingjob SET indexer = 'StreetIndex' WHERE indexer = 'Street'"),
migrations.RunSQL("UPDATE maposmatic_maprenderingjob SET indexer = 'PoiIndex' WHERE indexer = 'Poi'"),
]

Wyświetl plik

@ -45,24 +45,11 @@ def get_poi_file_path(instance, filename):
return ""
class MapRenderingJobManager(models.Manager):
def to_render(self):
return MapRenderingJob.objects.filter(status=0).order_by('submission_time')
def to_render(self, queue_name = 'default'):
return MapRenderingJob.objects.filter(status=0).filter(queue=queue_name).order_by('submission_time')
def queue_size(self):
return MapRenderingJob.objects.filter(status=0).count()
# We try to find a rendered map from the last 15 days, which still
# has its thumbnail present.
def get_random_with_thumbnail(self):
fifteen_days_before = datetime.now() - timedelta(15)
maps = (MapRenderingJob.objects.filter(status=2)
.filter(submission_time__gte=fifteen_days_before)
.filter(resultmsg='ok')
.order_by('?')[0:10])
for m in maps:
if m.get_thumbnail():
return m
return None
def queue_size(self, queue_name = 'default'):
return MapRenderingJob.objects.filter(status=0).filter(queue=queue_name).count()
def get_by_filename(self, name):
"""Tries to find the parent MapRenderingJob of a given file from its
@ -98,6 +85,7 @@ class MapRenderingJob(models.Model):
stylesheet = models.CharField(max_length=256)
overlay = models.CharField(max_length=256, null=True, blank=True)
layout = models.CharField(max_length=256)
indexer = models.CharField(max_length=256, null=True)
paper_width_mm = models.IntegerField()
paper_height_mm = models.IntegerField()
bitmap_dpi = models.IntegerField(default=72)
@ -126,6 +114,8 @@ class MapRenderingJob(models.Model):
renderstep = models.CharField(max_length=80,null=True,blank=True)
queue = models.CharField(max_length=40,null=False,blank=False, default='default')
nonce = models.CharField(max_length=NONCE_SIZE, blank=True)
class Meta:
@ -325,17 +315,7 @@ class MapRenderingJob(models.Model):
return None
def current_position_in_queue(self):
return MapRenderingJob.objects.filter(status=0).filter(id__lte=self.id).count()
# Estimate the date at which the rendering will be started
def rendering_estimated_start_time(self):
waiting_time = datetime.now() - self.submission_time
progression = self.index_queue_at_submission - self.current_position_in_queue()
if progression == 0:
return datetime.now()
mean_job_rendering_time = waiting_time // progression
estimated_time_left = mean_job_rendering_time * self.current_position_in_queue()
return datetime.now() + estimated_time_left
return MapRenderingJob.objects.filter(status=0).filter(queue=self.queue).filter(id__lte=self.id).count()
def get_absolute_url(self):
return reverse('map-by-id', args=[self.id])

Wyświetl plik

@ -11,9 +11,9 @@
data-html="true"
data-original-title="{% trans "Platform status" %}"
data-placement="bottom"
data-content='<p>{% trans "Rendering daemon: " %}<i class="pull-right fas fa-{% if daemon_running %}check{% else %}times{% endif %}"></i></p>
<p>{% trans "GIS database: " %}<i class="pull-right fas fa-{% if gis_lag_ok %}check{% else %}{% if gis_lastupdate %}warning{% else %}times{% endif %}{% endif %}"></i></p>
<p>{% trans "WayMarked database: " %}<i class="pull-right fas fa-{% if waymarked_lag_ok %}check{% else %}{% if waymarked_lastupdate %}warning{% else %}times{% endif %}{% endif %}"></i></p>'
data-content='<p class="text-{{queues_overall_state}}">{% trans "Rendering queue(s): " %}<i class="pull-right fas fa-{{queues_overall_symbol}}"></i></p>
<p class="text-{% if gis_lag_ok %}success{% else %}{% if gis_lastupdate %}warning{% else %}danger{% endif %}{% endif %}">{% trans "GIS database: " %}<i class="pull-right fas fa-{% if gis_lag_ok %}check{% else %}{% if gis_lastupdate %}hourglass-half{% else %}times{% endif %}{% endif %}"></i></p>
<p class="text-{% if waymarked_lag_ok %}success{% else %}{% if waymarked_lastupdate %}warning{% else %}danger{% endif %}{% endif %}">{% trans "WayMarked database: " %}<i class="pull-right fas fa-{% if waymarked_lag_ok %}check{% else %}{% if waymarked_lastupdate %}hourglass-half{% else %}times{% endif %}{% endif %}"></i></p>'
class="navbar-brand popovered" href="#"><i class="fas fa-{{ platform_status }}"></i> {{BRAND_NAME}}</a>
<button class="navbar-toggler" type="button" data-toggle="collapse" data-target="#navbar" aria-controls="navbar-collapse" aria-expanded="false" aria-label="{% trans "Toggle navigation" %}">
@ -22,63 +22,63 @@
<div id="navbar" class="navbar-collapse collapse justify-content-end">
<ul class="navbar-nav mr-auto">
<li class="nav-item {% block menu-home %}{% endblock %}"><a class="nav-link" href="{% url "main" %}"><i class="fas fa-home"></i> {% trans "Home" %}</a></li>
<li class="nav-item {% block menu-new %}{% endblock %}"><a class="nav-link" href="{% url "new" %}"><i class="fas fa-edit"></i> {% trans "Create map" %}</a></li>
<li class="nav-item {% block menu-maps %}{% endblock %}"><a class="nav-link" href="{% url "maps" %}"><i class="fas fa-list"></i> {% trans "Maps" %}</a></li>
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button"><i class="fas fa-comment"></i> {% trans "About" %} <b class="caret"></b></a>
<div class="dropdown-menu">
<a class="dropdown-item" href="{% url "about" %}"><i class="fas fa-comment"></i> {% trans "About Maposmatic" %}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url "documentation_user_guide" %}"><i class="fas fa-book"></i> {% trans "User Guide" %}</a>
<a class="dropdown-item" href="{% url "documentation_api" %}"><i class="fas fa-book"></i> {% trans "API Docs" %}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="https://github.com/hholzgra/maposmatic/" target="_blank"><i class="fab fa-lg fa-git-square"></i> MapOSMatic project</a>
<a class="dropdown-item" href="https://github.com/hholzgra/ocitysmap/" target="_blank"><i class="fab fa-lg fa-git-square"></i> OCitysMap project</a>
<a class="dropdown-item" href="https://github.com/hholzgra/maposmatic-vagrant/" target="_blank"><i class="fab fa-lg fa-git-square"></i> MapOSMatic Vagrant Box</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url "rss-feed" %}"><i class="fas fa-rss-square"></i> {% trans "Maps feed" %}</a>
<a class="dropdown-item" href="{% url "error-feed" %}"><i class="fas fa-rss-square"></i> {% trans "Errors feed" %}</a>
</div>
</li>
<li class="nav-item {% block menu-home %}{% endblock %}"><a class="nav-link" href="{% url "main" %}"><i class="fas fa-home"></i> {% trans "Home" %}</a></li>
<li class="nav-item {% block menu-new %}{% endblock %}"><a class="nav-link" href="{% url "new" %}"><i class="fas fa-edit"></i> {% trans "Create map" %}</a></li>
<li class="nav-item {% block menu-maps %}{% endblock %}"><a class="nav-link" href="{% url "maps" %}"><i class="fas fa-list"></i> {% trans "Maps" %}</a></li>
{% if PAYPAL_ID %}
<li class="nav-item {% block menu-donate %}{% endblock %}"><a class="nav-item" href="{% url "donate" %}"><i class="fas fa-gift"></i> {% trans "Donate" %}</a></li>
{% endif %}
<li class="nav-item dropdown">
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown" role="button"><i class="fas fa-comment"></i> {% trans "About" %} <b class="caret"></b></a>
<div class="dropdown-menu">
<a class="dropdown-item" href="{% url "about" %}"><i class="fas fa-comment"></i> {% trans "About Maposmatic" %}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url "documentation_user_guide" %}"><i class="fas fa-book"></i> {% trans "User Guide" %}</a>
<a class="dropdown-item" href="{% url "documentation_api" %}"><i class="fas fa-book"></i> {% trans "API Docs" %}</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="https://github.com/hholzgra/maposmatic/" target="_blank"><i class="fab fa-lg fa-git-square"></i> MapOSMatic project</a>
<a class="dropdown-item" href="https://github.com/hholzgra/ocitysmap/" target="_blank"><i class="fab fa-lg fa-git-square"></i> OCitysMap project</a>
<a class="dropdown-item" href="https://github.com/hholzgra/maposmatic-vagrant/" target="_blank"><i class="fab fa-lg fa-git-square"></i> MapOSMatic Vagrant Box</a>
<div class="dropdown-divider"></div>
<a class="dropdown-item" href="{% url "rss-feed" %}"><i class="fas fa-rss-square"></i> {% trans "Maps feed" %}</a>
<a class="dropdown-item" href="{% url "error-feed" %}"><i class="fas fa-rss-square"></i> {% trans "Errors feed" %}</a>
</div>
</li>
{% if PAYPAL_ID %}
<li class="nav-item {% block menu-donate %}{% endblock %}"><a class="nav-item" href="{% url "donate" %}"><i class="fas fa-gift"></i> {% trans "Donate" %}</a></li>
{% endif %}
<li class="nav-item dropdown">
<a href="#" class="nav-link dropdown-toggle" data-toggle="dropdown">
<span class="{{LANGUAGE_CODE|language_flag}}"> </span> {{ LANGUAGES|getitem:LANGUAGE_CODE }} <b class="caret"></b>
</a>
<div class="dropdown-menu bg-light">
<form action="/i18n/setlang/" method="post" name="langsel" class="form-inline form-horizontal">
<input name="next" type="hidden" value="{{ request.path }}" />
<input name="next" type="hidden" value="{{ request.path }}" />
<input name="language" type="hidden" value="{{ LANGUAGE_CODE }}" />
{% for language in LANGUAGES_LIST %}
<a class="dropdown-item" href="javascript:(function() { document.langsel.language.value = '{{ language.0 }}'; document.langsel.submit(); })();"><span class="{{language.0|language_flag}}"> </span> {{ language.1 }}</a>
{% endfor %}
</form>
</div>
</li>
</li>
{% if WEBLATE_BASE_URL %}
{% if LANGUAGE_CODE != "en" %}
<li class="nav-item">
<a class="nav-link" target=_blank" href="{{ WEBLATE_BASE_URL }}engage/maposmatic/?utm_source=widget">
<img src="{{ WEBLATE_BASE_URL }}widgets/maposmatic/{{ LANGUAGE_CODE }}/svg-badge.svg" alt="Translation status" />
</a>
</li>
{% if WEBLATE_BASE_URL %}
{% if LANGUAGE_CODE != "en" %}
<li class="nav-item">
<a class="nav-link" target=_blank" href="{{ WEBLATE_BASE_URL }}engage/maposmatic/?utm_source=widget">
<img src="{{ WEBLATE_BASE_URL }}widgets/maposmatic/{{ LANGUAGE_CODE }}/svg-badge.svg" alt="Translation status" />
</a>
</li>
{% endif %}
{% endif %}
{% endif %}
</ul>
<form action="{% url "maps" %}" method="get" class="form-inline">
<div class="md-form">
<i class="fas fa-search"></i>
{{ searchform.query }}
</div>
<div class="md-form">
<i class="fas fa-search"></i>
{{ searchform.query }}
</div>
</form>
</div>
</div>

Wyświetl plik

@ -49,6 +49,17 @@
<script src="/media/node_modules/bootstrap/dist/js/bootstrap.js"></script>
<link rel="stylesheet" href="/media/node_modules/bootstrap/dist/css/bootstrap.css" />
<!--
<link rel="stylesheet" href="/media/node_modules/mdbootstrap/css/bootstrap.css">
<link rel="stylesheet" href="/media/node_modules/mdbootstrap/css/mdb.min.css">
<link rel="stylesheet" href="/media/node_modules/mdbootstrap/css/style.css">
<script type="text/javascript" src="/media/node_modules/mdbootstrap/js/jquery.min.js"></script>
<script type="text/javascript" src="/media/node_modules/mdbootstrap/js/popper.min.js"></script>
<script type="text/javascript" src="/media/node_modules/mdbootstrap/js/bootstrap.min.js"></script>
<script type="text/javascript" src="/media/node_modules/mdbootstrap/js/mdb.min.js"></script>
-->
<script src="/media/node_modules/leaflet/dist/leaflet.js"></script>
<link rel="stylesheet" href="/media/node_modules/leaflet/dist/leaflet.css" />

Wyświetl plik

@ -0,0 +1,103 @@
{% extends "maposmatic/base.html" %}
{% comment %}
coding: utf-8
maposmatic, the web front-end of the MapOSMatic city map generation system
Copyright (C) 2022 Hartmut Holzgraefe
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU Affero General Public License as
published by the Free Software Foundation, either version 3 of the
License, or any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU Affero General Public License for more details.
You should have received a copy of the GNU Affero General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
{% endcomment %}
{% load i18n %}
{% load l10n %}
{% load extratags %}
{% block title %}{% trans "Congo Health Map" %}{% endblock %}
{% block page %}
<div class="treeview w-20 border">
<h6 class="pt-3 pl-3">Folders</h6>
<hr>
<ul class="mb-1 pl-3 pb-2">
<li><i class="fas fa-angle-right rotate"></i>
<span><i class="far fa-envelope-open ic-w mx-1"></i>Mail</span>
<ul class="nested">
<li><i class="far fa-bell ic-w mr-1"></i>Offers</li>
<li><i class="far fa-address-book ic-w mr-1"></i>Contacts</li>
<li><i class="fas fa-angle-right rotate"></i>
<span><i class="far fa-calendar-alt ic-w mx-1"></i>Calendar</span>
<ul class="nested">
<li><i class="far fa-clock ic-w mr-1"></i>Deadlines</li>
<li><i class="fas fa-users ic-w mr-1"></i>Meetings</li>
<li><i class="fas fa-basketball-ball ic-w mr-1"></i>Workouts</li>
<li><i class="fas fa-mug-hot ic-w mr-1"></i>Events</li>
</ul>
</li>
</ul>
</li>
<li><i class="fas fa-angle-right rotate"></i>
<span><i class="far fa-folder-open ic-w mx-1"></i>Inbox</span>
<ul class="nested">
<li><i class="far fa-folder-open ic-w mr-1"></i>Admin</li>
<li><i class="far fa-folder-open ic-w mr-1"></i>Corporate</li>
<li><i class="far fa-folder-open ic-w mr-1"></i>Finance</li>
<li><i class="far fa-folder-open ic-w mr-1"></i>Other</li>
</ul>
</li>
<li><i class="fas fa-angle-right rotate"></i>
<span><i class="far fa-gem ic-w mx-1"></i>Favourites</span>
<ul class="nested">
<li><i class="fas fa-pepper-hot ic-w mr-1"></i>Restaurants</li>
<li><i class="far fa-eye ic-w mr-1"></i>Places</li>
<li><i class="fas fa-gamepad ic-w mr-1"></i>Games</li>
<li><i class="fas fa-cocktail ic-w mr-1"></i>Coctails</li>
<li><i class="fas fa-pizza-slice ic-w mr-1"></i>Food</li>
</ul>
</li>
<li><i class="far fa-comment ic-w mr-1"></i>Notes</li>
<li><i class="fas fa-cogs ic-w mr-1"></i>Settings</li>
<li><i class="fas fa-desktop ic-w mr-1"></i>Devices</li>
<li><i class="fas fa-trash-alt ic-w mr-1"></i>Deleted Items</li>
</ul>
</div>
<!--
<div class="treeview w-20 border">
<h6 class="pt-3 pl-3">foobar</h6><hr/>
<ul class="mb-1 pl-3 pb-2">
<li>foo
<ul class="nested">
<li>foo1</li>
<li>foo2</li>
</ul>
</li>
<li>bar
<ul class="nested">
<li>bar1</li>
<li>bar2</li>
</ul>
</li>
</ul>
</div>
-->
{% endblock %}
{% block extrajs %}
$(document).ready(function() {
$('.treeview').mdbTreeview();
});
{% endblock %}

Wyświetl plik

@ -0,0 +1,60 @@
<html>
<head>
<title>Heatmap</title>
<script src="/media/node_modules/leaflet/dist/leaflet.js"></script>
<link rel="stylesheet" href="/media/node_modules/leaflet/dist/leaflet.css" />
<script src="/media/node_modules/heatmap.js/build/heatmap.min.js"></script>
<script src="/media/node_modules/leaflet-heatmap/leaflet-heatmap.js"></script>
<script src="/apis/heatdata/{{ days }}"></script>
</head>
<body>
<h1>Heatmap test 2x</h1>
<div class="demo-wrapper">
<div class="heatmap" id="map-canvas" style="height: 500px;">
</div>
</div>
<script>
window.onload = function() {
var baseLayer = L.tileLayer(
'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png',{
attribution: 'Map data &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, <a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, Imagery © <a href="http://cloudmade.com">CloudMade</a>',
maxZoom: 18
}
);
var cfg = {
// radius should be small ONLY if scaleRadius is true (or small radius is intended)
"radius": 10,
"maxOpacity": .8,
// scales the radius based on map zoom
"scaleRadius": false,
// if set to false the heatmap uses the global maximum for colorization
// if activated: uses the data maximum within the current map boundaries
// (there will always be a red spot with useLocalExtremas true)
"useLocalExtrema": true,
// which field name in your data represents the latitude - default "lat"
latField: 'lat',
// which field name in your data represents the longitude - default "lng"
lngField: 'lng',
// which field name in your data represents the data value - default "value"
valueField: 'count'
};
var heatmapLayer = new HeatmapOverlay(cfg);
var map = new L.Map('map-canvas', {
center: new L.LatLng(20,0),
zoom: 2,
layers: [baseLayer, heatmapLayer]
});
heatmapLayer.setData(data);
};
</script>
</body>
</html>

Wyświetl plik

@ -1,3 +1,7 @@
{% load i18n %}
{% load l10n %}
{% load extratags %}
<div id="carousel-example-generic" class="carousel slide" data-ride="carousel">
<!-- Indicators -->
<ol class="carousel-indicators">
@ -9,21 +13,30 @@
<!-- Wrapper for slides -->
<div class="carousel-inner">
<div class="carousel-item active">
<img src="/media/img/preview-1.png" width="100%" alt="plain layout">
<img
src="/media/img/layout/plain.png"
srcset="/media/img/layout/plain.png, /media/img/layout/plain-1.5x.png 1.5x, /media/img/layout/plain-2x.png 2x"
width="100%" alt="{% trans "Plain Layout" %}">
<div class="carousel-caption">
<h3>Plain Layout</h3>
<h3>{% trans "Plain Layout" %}</h3>
</div>
</div>
<div class="carousel-item">
<img src="/media/img/preview-2.png" width="100%" alt="side index layout">
<img
src="/media/img/layout/single_page_index_side.png"
srcset="/media/img/layout/single_page_index_side.png, /media/img/layout/single_page_index_side-1.5x.png 1.5x, /media/img/layout/single_page_index_side-2x.png 2x"
width="100%" alt="{% trans "Index Layout" %}">
<div class="carousel-caption">
<h3>Side Index Layout</h3>
<h3>{% trans "Index Layout" %}</h3>
</div>
</div>
<div class="carousel-item">
<img src="/media/img/preview-3.png" width="100%" alt="multi page layout">
<img
src="/media/img/layout/multi_page.png"
srcset="/media/img/layout/multi_page.png, /media/img/layout/multi_page-1.5x.png 1.5x, /media/img/layout/multi_page-2x.png 2x"
width="100%" alt="{% trans "Multi Page Layout" %}">
<div class="carousel-caption">
<h3>Multi Page Layout</h3>
<h3>{% trans "Multi Page Layout" %}</h3>
</div>
</div>
</div>
@ -31,11 +44,11 @@
<!-- Controls -->
<a class="carousel-control-prev" href="#carousel-example-generic" role="button" data-slide="prev">
<span class="carousel-control-prev-icon" aria-hidden="true"></span>
<span class="sr-only">Previous</span>
<span class="sr-only">{% trans "Previous" %}</span>
</a>
<a class="carousel-control-next" href="#carousel-example-generic" role="button" data-slide="next">
<span class="carousel-control-next-icon" aria-hidden="true"></span>
<span class="sr-only">Next</span>
<span class="sr-only">{% trans "Next" %}</span>
</a>
</div> <!-- Carousel -->

Wyświetl plik

@ -5,44 +5,43 @@
<div class="alert alert-info">
<h2>{% trans "Platform status" %}</h2>
{% if queued %}
<p class="queue">
{% blocktrans with queued|pluralize as qp %}{{ queued }} job{{ qp }} in the rendering queue.{% endblocktrans %}
</p>
{% endif %}
<p>
{% if daemon_running and gis_lag_ok and waymarked_lag_ok%}
{% blocktrans with gis_lastupdate|timesince:utc_now as date %}All systems are operational.{% endblocktrans %}
{% else %}
{% if daemon_running and gis_lastupdate %}
{% blocktrans %}Systems are operational but renderings may not reflect the latest available OpenStreetMap data.{% endblocktrans %}
{% endif %}
{% endif %}
{% if daemon_running %}
<div class="alert alert-success">
<i class="fas fa-check"></i> {% blocktrans %}The rendering daemon is running.{% endblocktrans %}
<div class="alert alert-{{queues_overall_state}}">
<h5>Render queue states:</h5>
<ul class="list-group">
{% for key, value in queue_states.items %}
<li class="list-group-item
list-group-item-{{value.running|yesno:"success,danger,warning"}}
d-flex justify-content-between align-items-center"
>{{key}}
<span
class="badge badge-pill
badge-{{value.running|yesno:"success,danger,warning"}}"
title="{% blocktrans with size=value.size qp=value.size|pluralize %} {{size}} job{{ qp }} in the rendering queue.{% endblocktrans %}">
{% if value.size > 0 %}
{{value.size}}
{% else %}
-
{% endif %}
</span>
</li>
{% endfor %}
</ul>
</div>
{% else %}
<div class="alert alert-danger">
<i class="fas fa-times"></i> {% blocktrans %}The rendering daemon is not running: jobs will be queued until the rendering daemon is back up.{% endblocktrans %}
</div>
{% endif %}
{% if gis_lag_ok %}
<div class="alert alert-success">
<i class="fas fa-check"></i> {% blocktrans with gis_lastupdate|timesince:utc_now as date %}The GIS database is online and up to date, <span class="tooltipped" data-original-title="{{ gis_lastupdate }}">updated {{ date }} ago</span>.{% endblocktrans %}
<i class="fas fa-check"></i> {% blocktrans with gis_lastupdate|timesince:utc_now as date %}The GIS database is online and up to date, <span class="tooltipped" data-original-title="{{ gis_lastupdate }}">updated {{ date }} ago</span>.{% endblocktrans %}
</div>
{% else %}
{% if gis_lastupdate %}
<div class="alert alert-warning">
<i class="fas fa-warning"></i>
{% blocktrans with gis_lastupdate|timesince:utc_now as date %}The GIS database is not up to date and was only <span class="tooltipped" data-original-title="{{ gis_lastupdate }}">updated {{ date }} ago</span>.{% endblocktrans %}
<i class="fas fa-warning"></i>
{% blocktrans with gis_lastupdate|timesince:utc_now as date %}The GIS database is not up to date and was only <span class="tooltipped" data-original-title="{{ gis_lastupdate }}">updated {{ date }} ago</span>.{% endblocktrans %}
</div>
{% else %}
<div class="alert alert-danger">
<i class="fas fa-times"></i> {% blocktrans %}The GIS database is not available. Renderings cannot be processed at this time.{% endblocktrans %}
<i class="fas fa-times"></i> {% blocktrans %}The GIS database is not available. Renderings cannot be processed at this time.{% endblocktrans %}
</div>
{% endif %}
{% endif %}

Wyświetl plik

@ -27,6 +27,10 @@
<tr><td><strong>{% trans "Overlays" %}</strong></td>
<td>{{ obj.overlay|add_blank_after_comma }}</td></tr>
{% endif %}
{% if obj.indexer %}
<tr><td><strong>{% trans "Indexer" %}</strong></td>
<td>{{ obj.indexer }}</td></tr>
{% endif %}
{% if obj.track %}
<tr><td><strong>{% trans "GPX Track" %}</strong></td>
<td>{{ obj.track|file_basename }}</td></tr>

Wyświetl plik

@ -40,10 +40,11 @@
<div style="height: 2em"></div>
{% if map.needs_waiting %}
<div id="queue-progress" class="progress progress-striped active">
<div class="bar" style="width: {{ progress }}%; text-align: right; padding-right: 10px;">
{{ map.current_position_in_queue }} / {{ queue_size }}
</div>
<div class="progress">
<div id="queue-progress" class="progress-bar progress-bar-striped progress-bar-animated"" role="progressbar"
style="width: {{ progress }}%" aria-valuenow="{{ progress }}" aria-valuemin="0" aria-valuemax="100">
<span class="text-dark font-weight-bold">{{ map.current_position_in_queue }} {% trans "jobs ahead of us" %}</span>
</div>
</div>
{% endif %}

Wyświetl plik

@ -85,6 +85,10 @@
<tr><td><strong>{% trans "Overlays" %}</strong></td>
<td>{{ map.overlay|add_blank_after_comma }}</td></tr>
{% endif %}
{% if map.indexer %}
<tr><td><strong>{% trans "Indexer" %}</strong></td>
<td>{{ map.indexer }}</td></tr>
{% endif %}
{% if map.track %}
<tr><td><strong>{% trans "GPX Track" %}</strong></td>
<td>{{ map.track|file_basename }}</td></tr>

Wyświetl plik

@ -76,6 +76,9 @@
{% if map.overlay %}
<tr><td><strong>{% trans "Overlay(s)" %}</strong></td><td>{{ map.overlay|add_blank_after_comma }}</td></tr>
{% endif %}
{% if map.indexer %}
<tr><td><strong>{% trans "Indexer" %}</strong></td><td>{{ map.indexer }}</td></tr>
{% endif %}
{% if map.track %}
<tr><td><strong>{% trans "GPX track" %}</strong></td><td>{{ map.track|file_basename }}</td></tr>
{% endif %}
@ -125,6 +128,7 @@
{% endif %}
{% if map.needs_waiting and map.current_position_in_queue %}
<p class="queue-position">#{{ map.current_position_in_queue }}</p>
<p class="queue-name">{% trans "in queue:" %} {{ map.queue }}</p>
{% endif %}
</div>
</div>

Wyświetl plik

@ -247,9 +247,16 @@ $('#error-modal').modal('show')
<legend>{% trans "Layout" %}</legend>
{{ form.layout }}
</fieldset>
<fieldset>
<legend>{% trans "Indexer" %}</legend>
{{ form.indexer }}
</fieldset>
</div>
<div class="col-lg-6">
<img id="layout-preview" align="right" src="/media/img/layout/{{request.session.new_layout}}.png"/>
<img id="layout-preview" align="right"
src="/media/img/layout/{{request.session.new_layout}}.png"
srcset="/media/img/layout/{{request.session.new_layout}}.png, /media/img/layout/{{request.session.new_layout}}-1.5x.png 1.5x, /media/img/layout/{{request.session.new_layout}}-2x.png 2x"
/>
</div>
</div>

Wyświetl plik

@ -30,3 +30,9 @@ function metric_dist(lat1, lon1, lat2, lon2)
return Math.acos(dist) * 10000 / 90;
}
function load_preview(id, base)
{
var base2 = "/media/img/layout/" + base;
$(id).attr("src", base2 + ".png");
$(id).attr("srcset", base2 + ".png, " + base2 + "-1.5x.png 1.5x, " + base2 + "-2x.png 2x");
}

Wyświetl plik

@ -22,7 +22,7 @@ function choose_paper_buttons(layout)
$('#multi_page_sizes').hide();
}
}
var best_fit_width = 0;
@ -34,7 +34,7 @@ function preparePaperSize() {
$('#nextlink').show();
return;
}
$('#paper-wait').show();
$('#paper-size').hide();
$('#paper-size-loading-error').hide();
@ -56,6 +56,15 @@ function preparePaperSize() {
}
args['layout'] = $('input[name=layout]:checked').val();
if (!args['layout']) {
args['layout'] = $('#id_layout :selected').val();
}
args['indexer'] = $('input[name=indexer]:checked').val();
if (!args['indexer']) {
args['indexer'] = $('#id_indexer :selected').val();
}
args['stylesheet'] = $('input[name=stylesheet]:checked').val();
if (!args['stylesheet']) {
args['stylesheet'] = $('#id_stylesheet :selected').val();
@ -77,7 +86,7 @@ function preparePaperSize() {
disable_all_papersizes();
choose_paper_buttons(args['layout']);
for (i in data) {
var w = data[i]['width'];
var h = data[i]['height'];
@ -89,8 +98,8 @@ function preparePaperSize() {
best_fit_width = parseInt(w);
best_fit_height = parseInt(h);
best_fit_scale = parseInt( data[i][(data[i]['portrait_ok']) ? 'portrait_scale' : 'landscape_scale']);
best_fit_scale = parseInt( data[i][(data[i]['portrait_ok']) ? 'portrait_scale' : 'landscape_scale']);
$("#best_width").text(w);
$("#best_height").text(h);
@ -323,15 +332,15 @@ function show_paper_preview(canvas_name, w, h, scale)
// show actual selected area
ctx.fillStyle = "#DEDEFF";
if (scale) { // only set on single page formats
if (scale) { // only set on single page formats
var canvas_aspect = tw / th;
var selection_aspect = best_fit_width / best_fit_height;
var aw = tw;
var ah = th;
var oh = 0;
var ow = 0;
if (selection_aspect > canvas_aspect) {
ah = ah * canvas_aspect / selection_aspect;
oh = (th - ah) / 2;
@ -339,12 +348,12 @@ function show_paper_preview(canvas_name, w, h, scale)
aw = aw * selection_aspect / canvas_aspect;
ow = (tw - aw) / 2;
}
ctx.fillRect(dw + ow, dh + oh, aw, ah);
} else {
ctx.fillRect(dw, dh, tw, th);
}
// dashed paper diagonals
ctx.beginPath();
ctx.strokeStyle = "#000000";
@ -364,7 +373,7 @@ function show_paper_preview(canvas_name, w, h, scale)
ctx.fillText('ca. 1:' + scale, dw+tw/2, dh+th/4);
ctx.fillText('zoom '+ scaleDenominator2zoom(scale), dw+tw/2, dh+3*th/4);
}
// the actual paper size rectangle frame
ctx.strokeStyle = "#000000";
ctx.setLineDash([]);
@ -376,13 +385,13 @@ function show_paper_preview(canvas_name, w, h, scale)
function enable_button(buttons, txt)
{
for (button of buttons)
{
{
button.classList.remove("btn-success");
button.classList.remove("btn-outline-secondary");
button.classList.add("btn-primary");
button.removeAttribute("disabled");
button.setAttribute("title", txt);
}
}
@ -390,11 +399,11 @@ function enable_button(buttons, txt)
function disable_button(buttons)
{
for (button of buttons)
{
{
button.classList.remove("btn-primary");
button.classList.remove("btn-success");
button.classList.add("btn-outline-secondary");
button.setAttribute("disabled", "");
button.setAttribute("title", "");
@ -404,7 +413,7 @@ function disable_button(buttons)
function mark_button(buttons)
{
for (button of buttons)
{
{
button.classList.remove("btn-primary");
button.classList.add("btn-success");
}
@ -413,7 +422,7 @@ function mark_button(buttons)
function unmark_button(buttons)
{
for (button of buttons)
{
{
button.classList.remove("btn-success");
button.classList.add("btn-primary");
}

Wyświetl plik

@ -79,9 +79,7 @@ def about(request):
return render(request,
'maposmatic/about.html',
{ 'form': form,
'queued': job_list.count()
}
{ }
)
def privacy(request):
@ -91,6 +89,13 @@ def privacy(request):
{ }
)
def congo(request):
"""The congo health map page."""
return render(request,
'maposmatic/congo.html',
{ }
)
def documentation_user_guide(request):
"""The user guide page."""
return render(request,
@ -117,9 +122,7 @@ def donate(request):
return render(request,
'maposmatic/donate.html',
{ 'form': form,
'queued': job_list.count()
}
{ }
)
def donate_thanks(request):
@ -146,6 +149,7 @@ def new(request):
form = forms.MapRenderingJobForm(request.POST, request.FILES)
if form.is_valid():
request.session['new_layout'] = form.cleaned_data.get('layout')
request.session['new_indexer'] = form.cleaned_data.get('indexer')
request.session['new_stylesheet'] = form.cleaned_data.get('stylesheet')
request.session['new_overlay'] = form.cleaned_data.get('overlay')
request.session['new_paper_width_mm'] = form.cleaned_data.get('paper_width_mm')
@ -156,6 +160,9 @@ def new(request):
job.stylesheet = form.cleaned_data.get('stylesheet')
job.overlay = ",".join(form.cleaned_data.get('overlay'))
job.layout = form.cleaned_data.get('layout')
if job.layout.startswith('multi'):
job.queue = 'multipage'
job.indexer = form.cleaned_data.get('indexer')
job.paper_width_mm = form.cleaned_data.get('paper_width_mm')
job.paper_height_mm = form.cleaned_data.get('paper_height_mm')
job.status = 0 # Submitted
@ -167,7 +174,7 @@ def new(request):
job.submitteremail = form.cleaned_data.get('submitteremail')
job.map_language = form.cleaned_data.get('map_language')
job.index_queue_at_submission = (models.MapRenderingJob.objects
.queue_size())
.queue_size(job.queue) + 1)
job.nonce = helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
job.save()
@ -193,6 +200,11 @@ def new(request):
else:
request.session['new_layout'] = oc.get_all_renderer_names()[0]
if not 'indexer' in init_vals and 'new_indexer' in request.session :
init_vals['indexer'] = request.session['new_indexer']
else:
request.session['new_indexer'] = 'Street' # TODO make configurable
if not 'stylesheet' in init_vals and 'new_stylesheet' in request.session:
init_vals['stylesheet'] = request.session['new_stylesheet']
else:
@ -252,11 +264,11 @@ def map_full(request, id, nonce=None):
isredirected = request.session.get('redirected', False)
request.session.pop('redirected', None)
queue_size = models.MapRenderingJob.objects.queue_size()
queue_size = job.index_queue_at_submission
progress = 100
if queue_size:
progress = 20 + int(80 * (queue_size -
job.current_position_in_queue()) / float(queue_size))
progress = int(100 * (queue_size -
job.current_position_in_queue()) / float(queue_size))
refresh = job.is_rendering() and \
www.settings.REFRESH_JOB_RENDERING or \
@ -326,9 +338,15 @@ def recreate(request):
newjob.lat_bottom_right = job.lat_bottom_right
newjob.lon_bottom_right = job.lon_bottom_right
newjob.layout = job.layout
newjob.indexer = job.indexer
newjob.stylesheet = job.stylesheet
newjob.overlay = job.overlay
newjob.layout = job.layout
newjob.queue = "default"
if job.layout.startswith('multi'):
newjob.queue = 'multipage'
newjob.paper_width_mm = job.paper_width_mm
newjob.paper_height_mm = job.paper_height_mm
@ -340,7 +358,7 @@ def recreate(request):
newjob.submittermail = None # TODO
newjob.map_language = job.map_language
newjob.index_queue_at_submission = (models.MapRenderingJob.objects
.queue_size())
.queue_size() + 1)
newjob.nonce = helpers.generate_nonce(models.MapRenderingJob.NONCE_SIZE)
newjob.save()
@ -392,6 +410,12 @@ def api_nominatim(request):
return HttpResponse(content=json.dumps(contents),
content_type='text/json')
def heatmap(request, days=7):
return render(request, 'maposmatic/heatmap.html',
{ 'days' : days ,
})
def api_nominatim_reverse(request, lat, lon):
"""Nominatim reverse geocoding query gateway."""
lat = float(lat)
@ -635,11 +659,11 @@ def api_rendering_status(request, id, nonce=None):
isredirected = request.session.get('redirected', False)
request.session.pop('redirected', None)
queue_size = models.MapRenderingJob.objects.queue_size()
queue_size = job.index_queue_at_submission
progress = 100
if queue_size:
progress = 20 + int(80 * (queue_size -
job.current_position_in_queue()) / float(queue_size))
progress = int(100 * (queue_size -
job.current_position_in_queue()) / float(queue_size))
refresh = job.is_rendering() and \
www.settings.REFRESH_JOB_RENDERING or \

Wyświetl plik

@ -319,8 +319,17 @@ MAP_LANGUAGES_LIST.append(("C", _(u"No localization")))
REFRESH_JOB_WAITING = 15
REFRESH_JOB_RENDERING = 10
def is_daemon_running():
return 0 == os.system('systemctl is-active maposmatic-render.service')
QUEUE_NAMES = ['default', 'api', 'multipage']
# We are having this check in the settings file as it is somewhat
# platform specific. The below implementation assumes that the
# renderer process(es) are running as systemD services which
# should be a sensible default these days.
def is_daemon_running(queue_name=None):
name = ''
if queue_name is not None:
name = "@" + queue_name
return 0 == os.system('systemctl is-active maposmatic-render%s.service' % name)
# Logging
logconfig.setup_maposmatic_logging(

Wyświetl plik

@ -319,9 +319,20 @@ a.carousel-control { display: none; }
padding-bottom: 10px;
}
.queue-position { position: absolute;
top: -15px; right: 0; color: #eee; text-align: right;
font-weight: bold; line-height: 100pt; font-size: 100pt;
letter-spacing: -2px; vertical-align: top; z-index: 999;
.queue-position {
color: #eee;
text-align: center;
font-weight: bold;
font-size: 60pt;
letter-spacing: -2px;
align: center;
}
.queue-name {
color: #7f7f7f;
text-align: center;
font-size: 16pt;
letter-spacing: -2px;
align: center;
}

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 406 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 698 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 91 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 188 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 724 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.2 MiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 47 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 341 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 724 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.2 MiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 44 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 341 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 639 KiB

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 1.0 MiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 43 KiB

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 299 KiB

Wyświetl plik

@ -0,0 +1,2 @@
*.jpg
*.png

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 53 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 59 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 16 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 57 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 72 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 51 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 30 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 51 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 94 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 78 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 140 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 60 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 54 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 35 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 75 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 54 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 66 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 63 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 66 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 58 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 53 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 67 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 65 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 52 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 49 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 48 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 51 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 49 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 36 KiB

Wyświetl plik

@ -0,0 +1,2 @@
*.jpg
*.png

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 75 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 156 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 47 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 49 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 106 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 71 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 157 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 158 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 51 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 82 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 20 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 77 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 77 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 79 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 72 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 78 KiB

Plik binarny nie jest wyświetlany.

Przed

Szerokość:  |  Wysokość:  |  Rozmiar: 77 KiB

Some files were not shown because too many files have changed in this diff Show More