From fe70dcca77b254b615780bf973a86ff9e6b1f991 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Tue, 19 Oct 2021 13:33:24 +0200 Subject: [PATCH 01/18] mapfish experiments --- pdf-map-export/README.md | 19 ++ pdf-map-export/config.yaml | 14 ++ pdf-map-export/query.json | 471 ++++++++++++++++++++++++++++++++++++ pdf-map-export/report.jrxml | 35 +++ 4 files changed, 539 insertions(+) create mode 100644 pdf-map-export/README.md create mode 100644 pdf-map-export/config.yaml create mode 100644 pdf-map-export/query.json create mode 100644 pdf-map-export/report.jrxml diff --git a/pdf-map-export/README.md b/pdf-map-export/README.md new file mode 100644 index 00000000..8a0ab354 --- /dev/null +++ b/pdf-map-export/README.md @@ -0,0 +1,19 @@ +# Lokal MapFish Export + + + +## 1) Run docker: + +`docker run --name=mapfish-print-test --mount type=bind,source=./pdf-map-export,target=/usr/local/tomcat/webapps/ROOT/print-apps/swisstopo --publish=8080:8080 camptocamp/mapfish_print` + + + +## 2) Open localhost:8080 + +Copy `query.json` to input field and print a PDF + + + +## Some notes about the query + +The query uses "projection": "EPSG:2056", i.g. it uses the LV95 coordinate format. Do not modify the "matrices" attribute! \ No newline at end of file diff --git a/pdf-map-export/config.yaml b/pdf-map-export/config.yaml new file mode 100644 index 00000000..e8e897ed --- /dev/null +++ b/pdf-map-export/config.yaml @@ -0,0 +1,14 @@ +templates: + #=========================================================================== + A4 landscape: !template + #=========================================================================== + reportTemplate: report.jrxml + attributes: + map: !map + maxDpi: 400 + width: 780 + height: 330 + processors: + - !reportBuilder # compile all reports in current directory + directory: '.' + - !createMap {} \ No newline at end of file diff --git a/pdf-map-export/query.json b/pdf-map-export/query.json new file mode 100644 index 00000000..ea9511fb --- /dev/null +++ b/pdf-map-export/query.json @@ -0,0 +1,471 @@ +{ + "layout": "A4 landscape", + "outputFormat": "pdf", + "attributes": { + "map": { + "center": [ + 2631750, + 1189000 + ], + "scale": 25000, + "dpi": 254, + "pdfA": true, + "projection": "EPSG:2056", + "rotation": 0, + "layers": [ + { + "baseURL": "https://wmts100.geo.admin.ch/1.0.0/{Layer}/{style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.jpeg", + "dimensions": [ + "Time" + ], + "dimensionParams": { + "Time": "current" + }, + "name": "pixelkarte-farbe", + "imageFormat": "image/jpeg", + "layer": "ch.swisstopo.pixelkarte-farbe", + "matrices": [ + { + "identifier": "0", + "scaleDenominator": 14285714.285714287, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "1", + "scaleDenominator": 13392857.142857144, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "2", + "scaleDenominator": 12500000.000000002, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "3", + "scaleDenominator": 11607142.857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "4", + "scaleDenominator": 10714285.714285715, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "5", + "scaleDenominator": 9821428.571428573, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "6", + "scaleDenominator": 8928571.42857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "7", + "scaleDenominator": 8035714.285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "8", + "scaleDenominator": 7142857.142857144, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "9", + "scaleDenominator": 6250000.000000001, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 0 + ] + }, + { + "identifier": "10", + "scaleDenominator": 5357142.857142857, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 0 + ] + }, + { + "identifier": "11", + "scaleDenominator": 4464285.714285715, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 0 + ] + }, + { + "identifier": "12", + "scaleDenominator": 3571428.571428572, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 1 + ] + }, + { + "identifier": "13", + "scaleDenominator": 2678571.4285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 2, + 1 + ] + }, + { + "identifier": "14", + "scaleDenominator": 2321428.571428572, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 2, + 1 + ] + }, + { + "identifier": "15", + "scaleDenominator": 1785714.285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 3, + 2 + ] + }, + { + "identifier": "16", + "scaleDenominator": 892857.142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 7, + 4 + ] + }, + { + "identifier": "17", + "scaleDenominator": 357142.85714285716, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 18, + 12 + ] + }, + { + "identifier": "18", + "scaleDenominator": 178571.42857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 37, + 24 + ] + }, + { + "identifier": "19", + "scaleDenominator": 71428.57142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 93, + 62 + ] + }, + { + "identifier": "20", + "scaleDenominator": 35714.28571428572, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 187, + 124 + ] + }, + { + "identifier": "21", + "scaleDenominator": 17857.14285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 374, + 249 + ] + }, + { + "identifier": "22", + "scaleDenominator": 8928.57142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 749, + 499 + ] + }, + { + "identifier": "23", + "scaleDenominator": 7142.857142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 937, + 624 + ] + }, + { + "identifier": "24", + "scaleDenominator": 5357.142857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1249, + 833 + ] + }, + { + "identifier": "25", + "scaleDenominator": 3571.4285714285716, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1874, + 1249 + ] + }, + { + "identifier": "26", + "scaleDenominator": 1785.7142857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 3749, + 2499 + ] + } + ], + "matrixSet": "2056", + "opacity": 1, + "requestEncoding": "REST", + "style": "default", + "type": "WMTS", + "version": "1.0.0" + } + ] + } + } + } \ No newline at end of file diff --git a/pdf-map-export/report.jrxml b/pdf-map-export/report.jrxml new file mode 100644 index 00000000..17356f6d --- /dev/null +++ b/pdf-map-export/report.jrxml @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + + <band height="390" splitType="Stretch"> + <staticText> + <reportElement x="0" y="1" width="800" height="50" uuid="3988392b-f1e2-4eda-9cb4-7caa2b3eb0cc"/> + <textElement textAlignment="Center"> + <font size="36"/> + </textElement> + <text><![CDATA[Map]]></text> + </staticText> + <subreport> + <reportElement x="0" y="51" width="780" height="330" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <property name="local_mesure_unitwidth" value="pixel"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="local_mesure_unitheight" value="pixel"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <subreportExpression><![CDATA[$P{mapSubReport}]]></subreportExpression> + </subreport> + </band> + + \ No newline at end of file From e3c69455612e4f4efefac8094cd8b2429bacc28f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Tue, 19 Oct 2021 14:02:52 +0200 Subject: [PATCH 02/18] example query with all features needed --- pdf-map-export/query.json | 148 ++++++++++++++++++++++++++++++++++++-- 1 file changed, 144 insertions(+), 4 deletions(-) diff --git a/pdf-map-export/query.json b/pdf-map-export/query.json index ea9511fb..052ca519 100644 --- a/pdf-map-export/query.json +++ b/pdf-map-export/query.json @@ -4,15 +4,155 @@ "attributes": { "map": { "center": [ - 2631750, - 1189000 + 2732093, + 1198227 ], "scale": 25000, - "dpi": 254, + "dpi": 400, "pdfA": true, "projection": "EPSG:2056", "rotation": 0, "layers": [ + { + "geoJson": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": [ + [ + 2732093, + 1198227 + ], + [ + 2732200, + 1198226 + ], + [ + 2732600, + 1198026 + ], + [ + 2732200, + 1199226 + ] + ] + }, + "properties": { + "_ngeo_style": "1,2" + }, + "id": 7772936 + } + ] + }, + "opacity": 1, + "style": { + "version": 2, + "[_ngeo_style = '1']": { + "symbolizers": [ + { + "type": "line", + "strokeColor": "#ffffff", + "strokeOpacity": 0.01, + "strokeWidth": 2.5 + } + ] + }, + "[_ngeo_style = '2']": { + "symbolizers": [ + { + "type": "line", + "strokeColor": "#e30613", + "strokeOpacity": 0.5, + "strokeWidth": 2.5 + } + ] + }, + "[_ngeo_style = '1,2']": { + "symbolizers": [ + { + "type": "line", + "strokeColor": "#ffffff", + "strokeOpacity": 0.01, + "strokeWidth": 2.5 + }, + { + "type": "line", + "strokeColor": "#e30613", + "strokeOpacity": 0.5, + "strokeWidth": 2.5 + } + ] + } + }, + "type": "geojson", + "name": "selected track" + }, + { + "type": "grid", + "gridType": "LINES", + "numberOfLines": [5, 5], + "renderAsSvg": true, + "haloColor": "#CCFFCC", + "labelColor": "black", + "labelFormat": "%1.0f %s", + "indent": 10, + "haloRadius": 4, + "font": { + "name": [ + "Liberation Sans", + "Helvetica", + "Nimbus Sans L", + "Liberation Sans", + "FreeSans", + "Sans-serif" + ], + "size": 8, + "style": "BOLD" + } + }, + { + "geoJson": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [ + 2732393, + 1198227 + ] + }, + "properties": { + "_ngeo_style": "1" + }, + "id": 1596274 + } + ] + }, + "opacity": 1, + "style": { + "version": 2, + "[_ngeo_style = '1']": { + "symbolizers": [ + { + "type": "point", + "externalGraphic": "https://www.ref-kirche-zurzach.ch/wp-content/uploads/2018/10/cevi.png", + "graphicOpacity": 1, + "graphicWidth": 20, + "graphicXOffset": 0, + "graphicYOffset": -35, + "rotation": 0 + } + ] + } + }, + "type": "geojson", + "name": "selected track pois" + }, { "baseURL": "https://wmts100.geo.admin.ch/1.0.0/{Layer}/{style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.jpeg", "dimensions": [ @@ -459,7 +599,7 @@ } ], "matrixSet": "2056", - "opacity": 1, + "opacity": 0.85, "requestEncoding": "REST", "style": "default", "type": "WMTS", From 800383a217bfba2d29d3f7fc1e1441d1669ff35c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Tue, 19 Oct 2021 17:54:29 +0200 Subject: [PATCH 03/18] add logo and scalebar --- pdf-map-export/README.md | 3 ++- pdf-map-export/config.yaml | 11 ++++++--- pdf-map-export/report.jrxml | 45 ++++++++++++++++++++----------------- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/pdf-map-export/README.md b/pdf-map-export/README.md index 8a0ab354..5c00562e 100644 --- a/pdf-map-export/README.md +++ b/pdf-map-export/README.md @@ -16,4 +16,5 @@ Copy `query.json` to input field and print a PDF ## Some notes about the query -The query uses "projection": "EPSG:2056", i.g. it uses the LV95 coordinate format. Do not modify the "matrices" attribute! \ No newline at end of file +The query uses "projection": "EPSG:2056", i.g. it uses the LV95 coordinate format. Do not modify the "matrices" attribute! + diff --git a/pdf-map-export/config.yaml b/pdf-map-export/config.yaml index e8e897ed..173c5775 100644 --- a/pdf-map-export/config.yaml +++ b/pdf-map-export/config.yaml @@ -6,9 +6,14 @@ templates: attributes: map: !map maxDpi: 400 - width: 780 - height: 330 + width: 813 + height: 566 + scalebar: !scalebar + width: 230 + height: 40 processors: - !reportBuilder # compile all reports in current directory directory: '.' - - !createMap {} \ No newline at end of file + - !createMap {} + - !createScalebar {} + diff --git a/pdf-map-export/report.jrxml b/pdf-map-export/report.jrxml index 17356f6d..e1bcf5cd 100644 --- a/pdf-map-export/report.jrxml +++ b/pdf-map-export/report.jrxml @@ -1,35 +1,38 @@ - - + + - - - - - - - - + + - <band height="390" splitType="Stretch"> - <staticText> - <reportElement x="0" y="1" width="800" height="50" uuid="3988392b-f1e2-4eda-9cb4-7caa2b3eb0cc"/> - <textElement textAlignment="Center"> - <font size="36"/> - </textElement> - <text><![CDATA[Map]]></text> - </staticText> + <band height="567" splitType="Stretch"> <subreport> - <reportElement x="0" y="51" width="780" height="330" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <reportElement x="1" y="0" width="813" height="566" backcolor="#FFFFFF" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> <property name="local_mesure_unitwidth" value="pixel"/> - <property name="com.jaspersoft.studio.unit.width" value="px"/> <property name="local_mesure_unitheight" value="pixel"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> <property name="com.jaspersoft.studio.unit.height" value="px"/> </reportElement> <subreportExpression><![CDATA[$P{mapSubReport}]]></subreportExpression> </subreport> + <subreport> + <reportElement x="0" y="240" width="230" height="40" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"/> + <subreportExpression><![CDATA[$P{scalebarSubReport}]]></subreportExpression> + </subreport> + <staticText> + <reportElement x="730" y="550" width="84" height="16" uuid="a03c1801-86f0-4389-9fc7-3a5e6f7a30dc"/> + <textElement textAlignment="Center" verticalAlignment="Bottom"> + <font fontName="SansSerif" size="12"/> + </textElement> + <text><![CDATA[© swisstopo]]></text> + </staticText> + <image> + <reportElement x="564" y="500" width="250" height="60" uuid="27e1f678-4479-4220-ab68-3c240b8b2499"/> + <imageExpression><![CDATA["https://www.cevi.ch/files/cevi/images/logo-cevi.svg"]]></imageExpression> + </image> </band> - \ No newline at end of file + From 3a5a6a8424b207c6007a152321017cf25db2a4b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Wed, 20 Oct 2021 21:44:59 +0200 Subject: [PATCH 04/18] Update find_walk_table_points.py Update comments and sort imports --- .../automatic_walk_time_tables/find_walk_table_points.py | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/python_program/automatic_walk_time_tables/find_walk_table_points.py b/python_program/automatic_walk_time_tables/find_walk_table_points.py index 610537bc..f81af022 100644 --- a/python_program/automatic_walk_time_tables/find_walk_table_points.py +++ b/python_program/automatic_walk_time_tables/find_walk_table_points.py @@ -1,9 +1,8 @@ -from copy import copy -from typing import List, Tuple - import geopy.distance import gpxpy +from copy import copy from gpxpy.gpx import GPXTrackPoint +from typing import List, Tuple def select_waypoints(raw_gpx_data: gpxpy.gpx, walk_point_limit=21): @@ -11,6 +10,9 @@ def select_waypoints(raw_gpx_data: gpxpy.gpx, walk_point_limit=21): Algorithm that selects suitable points for the Marschzeittabelle. + Some parts are inspired by the Ramer–Douglas–Peucker algorithm. Especially + the third step, which reduces the number of points. + raw_gpx_data : raw gpx data from imported GPX-File walk_point_limit : max number of points in the walk-time table, default 21 From 073aed6f8074b5243e1f8f9d1168c7f2b58e07c5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Thu, 21 Oct 2021 20:17:08 +0200 Subject: [PATCH 05/18] basic print of route on map --- pdf-map-export/README.md | 2 +- python_program/__init__.py | 0 .../automatic_walk_time_tables/create_map.py | 147 ------ .../map_downloader/__init__.py | 0 .../map_downloader/create_map.py | 144 ++++++ .../map_downloader/matrices.json | 434 ++++++++++++++++++ python_program/main.py | 4 +- 7 files changed, 581 insertions(+), 150 deletions(-) create mode 100644 python_program/__init__.py delete mode 100644 python_program/automatic_walk_time_tables/create_map.py create mode 100644 python_program/automatic_walk_time_tables/map_downloader/__init__.py create mode 100644 python_program/automatic_walk_time_tables/map_downloader/create_map.py create mode 100644 python_program/automatic_walk_time_tables/map_downloader/matrices.json diff --git a/pdf-map-export/README.md b/pdf-map-export/README.md index 5c00562e..2bc9d468 100644 --- a/pdf-map-export/README.md +++ b/pdf-map-export/README.md @@ -4,7 +4,7 @@ ## 1) Run docker: -`docker run --name=mapfish-print-test --mount type=bind,source=./pdf-map-export,target=/usr/local/tomcat/webapps/ROOT/print-apps/swisstopo --publish=8080:8080 camptocamp/mapfish_print` +`docker run --name=mapfish-print-test --mount type=bind,source=/mnt/d/Projekte/Marschzeittabelle/pdf-map-export,target=/usr/local/tomcat/webapps/ROOT/print-apps/swisstopo --publish=8080:8080 camptocamp/mapfish_print` diff --git a/python_program/__init__.py b/python_program/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python_program/automatic_walk_time_tables/create_map.py b/python_program/automatic_walk_time_tables/create_map.py deleted file mode 100644 index 4cecfca1..00000000 --- a/python_program/automatic_walk_time_tables/create_map.py +++ /dev/null @@ -1,147 +0,0 @@ -import math -import os -from io import BytesIO - -import gpxpy -import grequests -import numpy as np -from PIL import Image, ImageDraw - -from . import coord_transformation -from . import gpx_utils - -TILE_SIZES = [64000, 25600, 12800, 5120, 2560, 1280, 640, 512, 384, 256, 128, 64, 25.6] -""" Tile width m, see https://api3.geo.admin.ch/services/sdiservices.html#wmts """ - -ZOOM_LEVELS = [16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28] -""" Zoom levels, see https://api3.geo.admin.ch/services/sdiservices.html#wmts """ - -TILE_WIDTH = 256 -""" Width of a tile in pixels """ - -TILE_MATRIX_SET: str = '2056' -""" EPSG code for LV03/CH1903 """ - - -def plot_route_on_map(raw_gpx_data: gpxpy.gpx, - way_points: [], - file_name: str, - open_figure: bool, - map_scaling: int, - layer: str = 'ch.swisstopo.pixelkarte-farbe', - tile_format_ext: str = 'jpeg'): - """ - - Creates a map of the route and marking the selected way points on it. - - raw_gpx_data : raw data form imported GPX file - way_points : selected way points of the walk-time table - tile_format_ext : Format of the tile, allowed values jpeg or png, default jpeg - layer : Map layer, see https://wmts.geo.admin.ch/EPSG/2056/1.0.0/WMTSCapabilities.xml for options - - """ - - # Todo: Implement user defined scaling! - if map_scaling is not None: - print('User defined map scaling will be ignored!') - - lv03_min, lv03_max = gpx_utils.calc_perimeter(raw_gpx_data) - - # zoom level of the map snippets (a value form 0 to 12) - zoom_level = 0 - - x_count = 0 - y_count = 0 - - while zoom_level < 12 and x_count * y_count < 36: - zoom_level = zoom_level + 1 - - # calc the number of the bottom left tile - tile_base_row = math.floor((lv03_min[0] - 420_000) / TILE_SIZES[zoom_level]) - 1 - tile_base_col = math.floor((350_000 - lv03_min[1]) / TILE_SIZES[zoom_level]) + 1 - - # calc number of tiles in each direction - x_count = math.ceil((lv03_max[0] - lv03_min[0]) / TILE_SIZES[zoom_level]) + 2 - y_count = math.ceil((lv03_max[1] - lv03_min[1]) / TILE_SIZES[zoom_level]) + 2 - - lv03_min = (tile_base_row * TILE_SIZES[zoom_level] + 420_000, 350_000 - tile_base_col * TILE_SIZES[zoom_level]) - - # load tiles and combine map parts - card_snippet_as_image = Image.new("RGBA", (TILE_WIDTH * x_count, TILE_WIDTH * y_count)) - - base_url = 'http://wmts.geo.admin.ch/1.0.0/' + layer + \ - '/default/current/' + TILE_MATRIX_SET + '/' + str(ZOOM_LEVELS[zoom_level]) + '/' - - # create urls - urls = [] - for i in range(0, x_count): - for j in range(1, y_count + 1): - urls.append(base_url + str(tile_base_row + i) + '/' + str(tile_base_col - j) + '.' + tile_format_ext) - - print('URL of a sample tile: ' + urls[0]) - - # Since swisstopo services are free, we must guarantee to use the service fairly - # See https://www.geo.admin.ch/de/geo-dienstleistungen/geodienste/terms-of-use.html - if len(urls) > 300: - raise Exception('Fair use limit exceeded!') - - # Request images in parallel - rs = (grequests.get(u) for u in urls) - results = grequests.map(rs) - - index = 0 - for i in range(0, x_count): - for j in range(1, y_count + 1): - response = results[index] - index = index + 1 - img = Image.open(BytesIO(response.content)) - img.thumbnail((TILE_WIDTH, TILE_WIDTH), Image.ANTIALIAS) - - w, h = img.size - card_snippet_as_image.paste(img, (i * w, h * (y_count - j), i * w + w, h * (y_count - j) + h)) - - # mark point on the map - draw = ImageDraw.Draw(card_snippet_as_image) - pixels_per_meter = (TILE_WIDTH / TILE_SIZES[zoom_level]) - - old_coords = None - for track in raw_gpx_data.tracks: - for segment in track.segments: - for point in segment.points: - wgs84_point = [point.latitude, point.longitude, point.elevation] - img_x, img_y = calc_img_coord(card_snippet_as_image.size, lv03_min, pixels_per_meter, wgs84_point) - - if old_coords is not None: - draw.line((old_coords, (img_x, img_y)), fill=(255, 165, 0), width=5) - - old_coords = (img_x, img_y) - - for point in way_points: - wgs84_point = [point[1].latitude, point[1].longitude, point[1].elevation] - img_x, img_y = calc_img_coord(card_snippet_as_image.size, lv03_min, pixels_per_meter, wgs84_point) - - pkt_rad = 10 - circle_coords = (img_x - pkt_rad, img_y - pkt_rad, img_x + pkt_rad, img_y + pkt_rad) - - draw.ellipse(circle_coords, outline=(255, 0, 0), width=5) - - # Check if output directory exists, if not, create it. - if (not os.path.exists('output')): - os.mkdir('output') - - # saves the image as '.jpg' - card_snippet_as_image.save('output/' + file_name + '_map.png') - - if open_figure: - card_snippet_as_image.show() - - -def calc_img_coord(image_size, lv03_min, pixels_per_meter, wgs84_point): - converter = coord_transformation.GPSConverter() - - lv03_point = np.round(converter.WGS84toLV03(wgs84_point[0], wgs84_point[1], wgs84_point[2])) - # calc the coords in respect to the image pixels - img_x = (lv03_point[0] - lv03_min[0]) * pixels_per_meter - img_y = image_size[1] - (lv03_point[1] - lv03_min[1]) * pixels_per_meter - - return img_x, img_y diff --git a/python_program/automatic_walk_time_tables/map_downloader/__init__.py b/python_program/automatic_walk_time_tables/map_downloader/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py new file mode 100644 index 00000000..80a8b027 --- /dev/null +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -0,0 +1,144 @@ +import json +import time +from pathlib import Path + +import gpxpy +import requests + +from .. import coord_transformation + + +def plot_route_on_map(raw_gpx_data: gpxpy.gpx, + way_points: [], + file_name: str, + open_figure: bool, + map_scaling: int, + layer: str = 'ch.swisstopo.pixelkarte-farbe', + print_api_base_url: str = 'localhost', + print_api_port: int = 8080, + print_api_protocol: str = 'http'): + """ + + Creates a map of the route and marking the selected way points on it. + + raw_gpx_data : raw data form imported GPX file + way_points : selected way points of the walk-time table + tile_format_ext : Format of the tile, allowed values jpeg or png, default jpeg + layer : Map layer, see https://wmts.geo.admin.ch/EPSG/2056/1.0.0/WMTSCapabilities.xml for options + + """ + + with open(str(Path(__file__).resolve().parent) + '/matrices.json') as json_file: + default_matrices = json.load(json_file) + + converter = coord_transformation.GPSConverter() + + path_coordinates = [] + + for track in raw_gpx_data.tracks: + for segment in track.segments: + for point in segment.points: + wgs84_point = [point.latitude, point.longitude, point.elevation] + lv03_point = converter.WGS84toLV03(wgs84_point[0], wgs84_point[1], wgs84_point[2]) + + path_coordinates.append([lv03_point[0] + 2_000_000, lv03_point[1] + 1_000_000]) + + print(path_coordinates[0]) + + query_json = { + "layout": "A4 landscape", + "outputFormat": "pdf", + "attributes": { + "map": { + "center": path_coordinates[0], + "scale": map_scaling, + "dpi": 400, + "pdfA": True, + "projection": "EPSG:2056", + "rotation": 0, + "layers": [ + { + "geoJson": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": path_coordinates + }, + "properties": { + "_ngeo_style": "1,2" + }, + "id": 7772936 + } + ] + }, + "opacity": 1, + "style": { + "version": 2, + "[_ngeo_style = '1,2']": { + "symbolizers": [ + { + "type": "line", + "strokeColor": "#e30613", + "strokeOpacity": 0.5, + "strokeWidth": 2.5 + }, + { + "type": "line", + "strokeColor": "#e30613", + "strokeOpacity": 0.75, + "strokeWidth": .5 + } + ] + } + }, + "type": "geojson", + "name": "selected track" + }, + { + "baseURL": "https://wmts100.geo.admin.ch/1.0.0/{Layer}/{style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.jpeg", + "dimensions": ["Time"], + "dimensionParams": {"Time": "current"}, + "name": layer, + "imageFormat": "image/jpeg", + "layer": layer, + "matrixSet": "2056", + "opacity": 0.85, + "requestEncoding": "REST", + "matrices": default_matrices, + "style": "default", + "type": "WMTS", + "version": "1.0.0" + } + ] + } + } + } + + base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) + url = '{}/print/default/report.pdf'.format(base_url) + response_obj = requests.post(url, data=json.dumps(query_json)) + + if response_obj.status_code != 200: + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + + response_json = json.loads(response_obj.content) + + pdf_status = requests.get(base_url + response_json['statusURL']) + while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': + time.sleep(0.5) + pdf_status = requests.get(base_url + response_json['statusURL']) + print(json.loads(pdf_status.content)['status']) + + if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + + fetched_pdf = requests.get(base_url + response_json['downloadURL']) + + if response_obj.status_code != 200: + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + + with open('output/{}_map.pdf'.format(file_name), 'wb') as f: + f.write(fetched_pdf.content) diff --git a/python_program/automatic_walk_time_tables/map_downloader/matrices.json b/python_program/automatic_walk_time_tables/map_downloader/matrices.json new file mode 100644 index 00000000..60a6e88c --- /dev/null +++ b/python_program/automatic_walk_time_tables/map_downloader/matrices.json @@ -0,0 +1,434 @@ +[ + { + "identifier": "0", + "scaleDenominator": 14285714.285714287, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "1", + "scaleDenominator": 13392857.142857144, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "2", + "scaleDenominator": 12500000.000000002, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "3", + "scaleDenominator": 11607142.857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "4", + "scaleDenominator": 10714285.714285715, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "5", + "scaleDenominator": 9821428.571428573, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "6", + "scaleDenominator": 8928571.42857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "7", + "scaleDenominator": 8035714.285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "8", + "scaleDenominator": 7142857.142857144, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 0, + 0 + ] + }, + { + "identifier": "9", + "scaleDenominator": 6250000.000000001, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 0 + ] + }, + { + "identifier": "10", + "scaleDenominator": 5357142.857142857, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 0 + ] + }, + { + "identifier": "11", + "scaleDenominator": 4464285.714285715, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 0 + ] + }, + { + "identifier": "12", + "scaleDenominator": 3571428.571428572, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1, + 1 + ] + }, + { + "identifier": "13", + "scaleDenominator": 2678571.4285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 2, + 1 + ] + }, + { + "identifier": "14", + "scaleDenominator": 2321428.571428572, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 2, + 1 + ] + }, + { + "identifier": "15", + "scaleDenominator": 1785714.285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 3, + 2 + ] + }, + { + "identifier": "16", + "scaleDenominator": 892857.142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 7, + 4 + ] + }, + { + "identifier": "17", + "scaleDenominator": 357142.85714285716, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 18, + 12 + ] + }, + { + "identifier": "18", + "scaleDenominator": 178571.42857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 37, + 24 + ] + }, + { + "identifier": "19", + "scaleDenominator": 71428.57142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 93, + 62 + ] + }, + { + "identifier": "20", + "scaleDenominator": 35714.28571428572, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 187, + 124 + ] + }, + { + "identifier": "21", + "scaleDenominator": 17857.14285714286, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 374, + 249 + ] + }, + { + "identifier": "22", + "scaleDenominator": 8928.57142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 749, + 499 + ] + }, + { + "identifier": "23", + "scaleDenominator": 7142.857142857143, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 937, + 624 + ] + }, + { + "identifier": "24", + "scaleDenominator": 5357.142857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1249, + 833 + ] + }, + { + "identifier": "25", + "scaleDenominator": 3571.4285714285716, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 1874, + 1249 + ] + }, + { + "identifier": "26", + "scaleDenominator": 1785.7142857142858, + "tileSize": [ + 256, + 256 + ], + "topLeftCorner": [ + 2420000, + 1350000 + ], + "matrixSize": [ + 3749, + 2499 + ] + } +] \ No newline at end of file diff --git a/python_program/main.py b/python_program/main.py index cb3ed2bd..f8066064 100644 --- a/python_program/main.py +++ b/python_program/main.py @@ -3,7 +3,7 @@ import gpxpy.gpx -from automatic_walk_time_tables.create_map import plot_route_on_map +from python_program.automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map from automatic_walk_time_tables.find_walk_table_points import select_waypoints from automatic_walk_time_tables.walk_table import plot_elevation_profile, create_walk_table from automatic_walk_time_tables.map_numbers import find_map_numbers @@ -36,7 +36,7 @@ def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_ 'default value.', default='./GPX/Default_Route.gpx') parser.add_argument('--velocity', type=float, default=3.75, help='Float. Speed in km/h on which the calculation is based, default 3.75 km/h.') - parser.add_argument('--map-scaling', type=int, + parser.add_argument('--map-scaling', type=int, default=25_000, help='Integer. Scaling of the created map (e.g. 10000 for scaling of 1:10\'000), if not ' 'specified the scaling will be automatically chosen.') parser.add_argument('--open-figure', default=False, action='store_true', From 8e1d7195e514b9e65ebcb6e7c192d8a94d15af19 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Fri, 22 Oct 2021 22:28:41 +0200 Subject: [PATCH 06/18] update map layout --- pdf-map-export/cevi_logo.svg | 2 ++ pdf-map-export/config.yaml | 14 ++++++++++++-- pdf-map-export/report.jrxml | 37 ++++++++++++++++++++++++------------ 3 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 pdf-map-export/cevi_logo.svg diff --git a/pdf-map-export/cevi_logo.svg b/pdf-map-export/cevi_logo.svg new file mode 100644 index 00000000..2fb5d659 --- /dev/null +++ b/pdf-map-export/cevi_logo.svg @@ -0,0 +1,2 @@ + + \ No newline at end of file diff --git a/pdf-map-export/config.yaml b/pdf-map-export/config.yaml index 173c5775..50193212 100644 --- a/pdf-map-export/config.yaml +++ b/pdf-map-export/config.yaml @@ -1,16 +1,26 @@ templates: + #=========================================================================== A4 landscape: !template #=========================================================================== + reportTemplate: report.jrxml attributes: + map: !map maxDpi: 400 width: 813 height: 566 + scalebar: !scalebar - width: 230 - height: 40 + width: 200 + height: 25 + default: + fontSize: 8 + renderAsSvg: True + unit: m + type: line + processors: - !reportBuilder # compile all reports in current directory directory: '.' diff --git a/pdf-map-export/report.jrxml b/pdf-map-export/report.jrxml index e1bcf5cd..bd931514 100644 --- a/pdf-map-export/report.jrxml +++ b/pdf-map-export/report.jrxml @@ -1,16 +1,14 @@ - + - <band height="567" splitType="Stretch"> <subreport> - <reportElement x="1" y="0" width="813" height="566" backcolor="#FFFFFF" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <reportElement x="1" y="0" width="813" height="566" backcolor="#B1F2BE" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> <property name="local_mesure_unitwidth" value="pixel"/> <property name="local_mesure_unitheight" value="pixel"/> <property name="com.jaspersoft.studio.unit.width" value="px"/> @@ -18,20 +16,35 @@ xsi:schemaLocation="http://jasperreports.sourceforge.net/jasperreports http://ja </reportElement> <subreportExpression><![CDATA[$P{mapSubReport}]]></subreportExpression> </subreport> + <rectangle> + <reportElement x="600" y="518" width="210" height="45" forecolor="#FFFFFF" uuid="c9901651-0788-428f-b7a3-0f1623195781"> + <property name="com.jaspersoft.studio.unit.x" value="px"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + <property name="com.jaspersoft.studio.unit.y" value="px"/> + </reportElement> + </rectangle> <subreport> - <reportElement x="0" y="240" width="230" height="40" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"/> + <reportElement x="605" y="523" width="200" height="25" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.x" value="px"/> + </reportElement> <subreportExpression><![CDATA[$P{scalebarSubReport}]]></subreportExpression> </subreport> <staticText> - <reportElement x="730" y="550" width="84" height="16" uuid="a03c1801-86f0-4389-9fc7-3a5e6f7a30dc"/> - <textElement textAlignment="Center" verticalAlignment="Bottom"> - <font fontName="SansSerif" size="12"/> + <reportElement x="605" y="545" width="200" height="16" uuid="a03c1801-86f0-4389-9fc7-3a5e6f7a30dc"> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.x" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font fontName="SansSerif" size="9"/> </textElement> - <text><![CDATA[© swisstopo]]></text> + <text><![CDATA[https://disclaimer.admin.ch © swisstopo]]></text> </staticText> - <image> - <reportElement x="564" y="500" width="250" height="60" uuid="27e1f678-4479-4220-ab68-3c240b8b2499"/> - <imageExpression><![CDATA["https://www.cevi.ch/files/cevi/images/logo-cevi.svg"]]></imageExpression> + <image evaluationTime="Report"> + <reportElement x="10" y="10" width="280" height="39" uuid="e7375b56-57f5-4abb-8f88-45734b26ed1b"/> + <imageExpression><![CDATA["cevi_logo.svg"]]></imageExpression> </image> </band> From 9519613f0a10ba6ca223f8308109531ca6412d85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Fri, 22 Oct 2021 22:30:31 +0200 Subject: [PATCH 07/18] implement downloading of map - migrated the request of maps numbers from grequests to requests, since those are not compatible and I will use requests for fetching the map --- .../map_downloader/create_map.py | 85 +++++++++++-------- ...atrices.json => default_map_matrices.json} | 0 .../automatic_walk_time_tables/map_numbers.py | 12 ++- python_program/main.py | 14 +-- 4 files changed, 64 insertions(+), 47 deletions(-) rename python_program/automatic_walk_time_tables/map_downloader/{matrices.json => default_map_matrices.json} (100%) diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index 80a8b027..baefbe80 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -25,25 +25,53 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, way_points : selected way points of the walk-time table tile_format_ext : Format of the tile, allowed values jpeg or png, default jpeg layer : Map layer, see https://wmts.geo.admin.ch/EPSG/2056/1.0.0/WMTSCapabilities.xml for options + print_api_base_url : host of the mapfish instance, default localhost + print_api_port : port for accessing mapfish, default 8080 + print_api_protocol : protocol used for accessing mapfish, default http """ - with open(str(Path(__file__).resolve().parent) + '/matrices.json') as json_file: - default_matrices = json.load(json_file) + query_json = create_mapfish_query(layer, map_scaling, raw_gpx_data) - converter = coord_transformation.GPSConverter() + base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) + url = '{}/print/default/report.pdf'.format(base_url) + response_obj = requests.post(url, data=json.dumps(query_json)) - path_coordinates = [] + if response_obj.status_code != 200: + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) - for track in raw_gpx_data.tracks: - for segment in track.segments: - for point in segment.points: - wgs84_point = [point.latitude, point.longitude, point.elevation] - lv03_point = converter.WGS84toLV03(wgs84_point[0], wgs84_point[1], wgs84_point[2]) + response_json = json.loads(response_obj.content) + + pdf_status = requests.get(base_url + response_json['statusURL']) + while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': + time.sleep(0.5) + pdf_status = requests.get(base_url + response_json['statusURL']) + print(json.loads(pdf_status.content)['status']) + + if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + + fetched_pdf = requests.get(base_url + response_json['downloadURL']) + + if response_obj.status_code != 200: + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + + with open('output/{}_map.pdf'.format(file_name), 'wb') as f: + f.write(fetched_pdf.content) - path_coordinates.append([lv03_point[0] + 2_000_000, lv03_point[1] + 1_000_000]) - print(path_coordinates[0]) +def create_mapfish_query(layer, map_scaling, raw_gpx_data): + """ + + Returns the JSON-Object used for querying + + """ + + path_coordinates = get_path_coordinates_as_list(raw_gpx_data) + + # load the default map matrices, used to inform mapfish about the available map scales and tile size + with open(str(Path(__file__).resolve().parent) + '/default_map_matrices.json') as json_file: + default_matrices = json.load(json_file) query_json = { "layout": "A4 landscape", @@ -116,29 +144,16 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, } } } + return query_json - base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) - url = '{}/print/default/report.pdf'.format(base_url) - response_obj = requests.post(url, data=json.dumps(query_json)) - - if response_obj.status_code != 200: - raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) - - response_json = json.loads(response_obj.content) - pdf_status = requests.get(base_url + response_json['statusURL']) - while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': - time.sleep(0.5) - pdf_status = requests.get(base_url + response_json['statusURL']) - print(json.loads(pdf_status.content)['status']) - - if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': - raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) - - fetched_pdf = requests.get(base_url + response_json['downloadURL']) - - if response_obj.status_code != 200: - raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) - - with open('output/{}_map.pdf'.format(file_name), 'wb') as f: - f.write(fetched_pdf.content) +def get_path_coordinates_as_list(raw_gpx_data): + path_coordinates = [] + converter = coord_transformation.GPSConverter() + for track in raw_gpx_data.tracks: + for segment in track.segments: + for point in segment.points: + wgs84_point = [point.latitude, point.longitude, point.elevation] + lv03_point = converter.WGS84toLV03(wgs84_point[0], wgs84_point[1], wgs84_point[2]) + path_coordinates.append([lv03_point[0] + 2_000_000, lv03_point[1] + 1_000_000]) + return path_coordinates diff --git a/python_program/automatic_walk_time_tables/map_downloader/matrices.json b/python_program/automatic_walk_time_tables/map_downloader/default_map_matrices.json similarity index 100% rename from python_program/automatic_walk_time_tables/map_downloader/matrices.json rename to python_program/automatic_walk_time_tables/map_downloader/default_map_matrices.json diff --git a/python_program/automatic_walk_time_tables/map_numbers.py b/python_program/automatic_walk_time_tables/map_numbers.py index cf60ba81..ff6db252 100644 --- a/python_program/automatic_walk_time_tables/map_numbers.py +++ b/python_program/automatic_walk_time_tables/map_numbers.py @@ -2,7 +2,7 @@ from typing import List import gpxpy -import grequests +import requests from . import coord_transformation @@ -51,14 +51,12 @@ def find_map_numbers(raw_gpx_data: gpxpy.gpx) -> str: # This means 1 request, which is below the fair use limit. url = base_url + f"geometry={start_point[0]},{start_point[1]}&imageDisplay=1283,937,96&mapExtent=2400000,1000000,2900000,1300000" - r = (grequests.get(url),) - results = grequests.map(r) + result = requests.get(url) all_maps = [] - for result in results: - data = json.loads(result.content) - for map in data["results"]: - all_maps.append([map["properties"]["name_de"], map["bbox"]]) + data = json.loads(result.content) + for map in data["results"]: + all_maps.append([map["properties"]["name_de"], map["bbox"]]) converter = coord_transformation.GPSConverter() needed_maps = set() diff --git a/python_program/main.py b/python_program/main.py index f8066064..126f335b 100644 --- a/python_program/main.py +++ b/python_program/main.py @@ -3,12 +3,14 @@ import gpxpy.gpx -from python_program.automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map from automatic_walk_time_tables.find_walk_table_points import select_waypoints -from automatic_walk_time_tables.walk_table import plot_elevation_profile, create_walk_table from automatic_walk_time_tables.map_numbers import find_map_numbers +from automatic_walk_time_tables.walk_table import plot_elevation_profile, create_walk_table +from python_program.automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map + -def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_figure: bool, map_scaling: int, creator_name: str): +def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_figure: bool, map_scaling: int, + creator_name: str): # Open GPX-File with the way-points gpx_file = open(gpx_file_path, 'r') raw_gpx_data = gpxpy.parse(gpx_file) @@ -21,7 +23,8 @@ def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_ # calc Points for walk table total_distance, temp_points, way_points = select_waypoints(raw_gpx_data) plot_elevation_profile(raw_gpx_data, way_points, temp_points, file_name=name, open_figure=open_figure) - create_walk_table(departure_date, velocity, way_points, total_distance, file_name=name, creator_name=creator_name, map_numbers=map_numbers) + create_walk_table(departure_date, velocity, way_points, total_distance, file_name=name, creator_name=creator_name, + map_numbers=map_numbers) plot_route_on_map(raw_gpx_data, way_points, file_name=name, open_figure=open_figure, map_scaling=map_scaling) @@ -45,7 +48,8 @@ def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_ parser.add_argument('--departure-time', type=lambda s: datetime.fromisoformat(s), default=datetime(year=2021, month=8, day=16, hour=9, minute=00), help='Departure date in ISO-format, i.g. 2011-11-04T00:05:23. Default 2021-08-16T09:00:00.') - parser.add_argument('--creator-name', type=str, help='Name of you, the creator of this route. If not specified, it will be empty.', default='') + parser.add_argument('--creator-name', type=str, + help='Name of you, the creator of this route. If not specified, it will be empty.', default='') args = parser.parse_args() From c53bb1eb8c86eb6e255575f820ebf23035aba318 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Sat, 23 Oct 2021 15:34:53 +0200 Subject: [PATCH 08/18] improve map layout --- pdf-map-export/config.yaml | 50 +- pdf-map-export/north_arrow.png | Bin 0 -> 6377 bytes pdf-map-export/query.json | 1202 ++++++++++++++++---------------- pdf-map-export/report.jrxml | 114 +-- 4 files changed, 721 insertions(+), 645 deletions(-) create mode 100644 pdf-map-export/north_arrow.png diff --git a/pdf-map-export/config.yaml b/pdf-map-export/config.yaml index 50193212..bfb6f086 100644 --- a/pdf-map-export/config.yaml +++ b/pdf-map-export/config.yaml @@ -2,7 +2,7 @@ templates: #=========================================================================== A4 landscape: !template - #=========================================================================== + #=========================================================================== reportTemplate: report.jrxml attributes: @@ -12,6 +12,41 @@ templates: width: 813 height: 566 + northArrow: !northArrow + size: 50 + default: + graphic: "north_arrow.png" + + overlayLayers: !staticLayers + default: + layers: + - type: grid + origin: [ 2700000, 1200000 ] + spacing: [ 1000, 1000 ] + renderAsSvg: true + gridColor: "#FFFFFF" + rotateLabels: False + labelColor: black + valueFormat: "###,###" + formatGroupingSeparator: " " + font: + name: + - Liberation Sans + - Helvetica + - Nimbus Sans L + - Liberation Sans + - FreeSans + - Sans-serif + size: 6 + style: + version: 2 + "*": + symbolizers: + - type: "line" + strokeWidth: 0.1 + strokeOpacity: 0.5 + strokeColor: gray + scalebar: !scalebar width: 200 height: 25 @@ -22,8 +57,13 @@ templates: type: line processors: - - !reportBuilder # compile all reports in current directory - directory: '.' - - !createMap {} - - !createScalebar {} + - !addOverlayLayers + inputMapper: + overlayLayers: staticLayers + map: map + - !reportBuilder # compile all reports in current directory + directory: '.' + - !createMap { } + - !createScalebar { } + - !createNorthArrow {} diff --git a/pdf-map-export/north_arrow.png b/pdf-map-export/north_arrow.png new file mode 100644 index 0000000000000000000000000000000000000000..fdf13f184e35b1389f4209f939751e31239b785b GIT binary patch literal 6377 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 - - - - - - - <band height="567" splitType="Stretch"> - <subreport> - <reportElement x="1" y="0" width="813" height="566" backcolor="#B1F2BE" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> - <property name="local_mesure_unitwidth" value="pixel"/> - <property name="local_mesure_unitheight" value="pixel"/> - <property name="com.jaspersoft.studio.unit.width" value="px"/> - <property name="com.jaspersoft.studio.unit.height" value="px"/> - </reportElement> - <subreportExpression><![CDATA[$P{mapSubReport}]]></subreportExpression> - </subreport> - <rectangle> - <reportElement x="600" y="518" width="210" height="45" forecolor="#FFFFFF" uuid="c9901651-0788-428f-b7a3-0f1623195781"> - <property name="com.jaspersoft.studio.unit.x" value="px"/> - <property name="com.jaspersoft.studio.unit.width" value="px"/> - <property name="com.jaspersoft.studio.unit.height" value="px"/> - <property name="com.jaspersoft.studio.unit.y" value="px"/> - </reportElement> - </rectangle> - <subreport> - <reportElement x="605" y="523" width="200" height="25" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> - <property name="com.jaspersoft.studio.unit.height" value="px"/> - <property name="com.jaspersoft.studio.unit.width" value="px"/> - <property name="com.jaspersoft.studio.unit.x" value="px"/> - </reportElement> - <subreportExpression><![CDATA[$P{scalebarSubReport}]]></subreportExpression> - </subreport> - <staticText> - <reportElement x="605" y="545" width="200" height="16" uuid="a03c1801-86f0-4389-9fc7-3a5e6f7a30dc"> - <property name="com.jaspersoft.studio.unit.width" value="px"/> - <property name="com.jaspersoft.studio.unit.x" value="px"/> - </reportElement> - <textElement textAlignment="Center" verticalAlignment="Middle"> - <font fontName="SansSerif" size="9"/> - </textElement> - <text><![CDATA[https://disclaimer.admin.ch © swisstopo]]></text> - </staticText> - <image evaluationTime="Report"> - <reportElement x="10" y="10" width="280" height="39" uuid="e7375b56-57f5-4abb-8f88-45734b26ed1b"/> - <imageExpression><![CDATA["cevi_logo.svg"]]></imageExpression> - </image> - </band> - + + + + + + + + <band height="567" splitType="Stretch"> + <subreport> + <reportElement x="1" y="0" width="813" height="566" backcolor="#B1F2BE" + uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <property name="local_mesure_unitwidth" value="pixel"/> + <property name="local_mesure_unitheight" value="pixel"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <subreportExpression><![CDATA[$P{mapSubReport}]]></subreportExpression> + </subreport> + <rectangle> + <reportElement x="600" y="518" width="210" height="45" forecolor="#FFFFFF" + uuid="c9901651-0788-428f-b7a3-0f1623195781"> + <property name="com.jaspersoft.studio.unit.x" value="px"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + <property name="com.jaspersoft.studio.unit.y" value="px"/> + </reportElement> + </rectangle> + <subreport> + <reportElement x="605" y="523" width="200" height="25" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.x" value="px"/> + </reportElement> + <subreportExpression><![CDATA[$P{scalebarSubReport}]]></subreportExpression> + </subreport> + <staticText> + <reportElement x="605" y="545" width="200" height="16" uuid="a03c1801-86f0-4389-9fc7-3a5e6f7a30dc"> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="com.jaspersoft.studio.unit.x" value="px"/> + </reportElement> + <textElement textAlignment="Center" verticalAlignment="Middle"> + <font fontName="SansSerif" size="9"/> + </textElement> + <text><![CDATA[https://disclaimer.admin.ch © swisstopo]]></text> + </staticText> + <image evaluationTime="Report"> + <reportElement x="10" y="10" width="280" height="39" uuid="e7375b56-57f5-4abb-8f88-45734b26ed1b"/> + <imageExpression><![CDATA["cevi_logo.svg"]]></imageExpression> + </image> + <image> + <reportElement x="750" y="20" width="50" height="50" uuid="fa145068-76a5-4834-98ed-ce65b1976b3d"> + <property name="local_mesure_unitwidth" value="pixel"/> + <property name="com.jaspersoft.studio.unit.width" value="px"/> + <property name="local_mesure_unitheight" value="pixel"/> + <property name="com.jaspersoft.studio.unit.height" value="px"/> + </reportElement> + <imageExpression class="net.sf.jasperreports.engine.JRRenderable"> + <![CDATA[net.sf.jasperreports.renderers.BatikRenderer.getInstance(new java.io.File(new java.net.URI($P{northArrowGraphic})))]]> + </imageExpression> + </image> + </band> + From 535fc28be997a339b969e5726e72b93b58682982 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Mon, 25 Oct 2021 19:37:48 +0200 Subject: [PATCH 09/18] added multi map export with custom scale #8 - first version of code, cleanup and optimizations needed! --- .../map_downloader/create_map.py | 127 ++++++++++++++---- 1 file changed, 104 insertions(+), 23 deletions(-) diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index baefbe80..b4a34a02 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -1,9 +1,13 @@ +import gpxpy import json +import matplotlib.pyplot as plt +import numpy as np +import requests import time +from matplotlib.patches import Rectangle from pathlib import Path - -import gpxpy -import requests +from pyclustering.cluster.kmeans import kmeans, kmeans_visualizer +from pyclustering.utils.metric import type_metric, distance_metric from .. import coord_transformation @@ -31,36 +35,45 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, """ - query_json = create_mapfish_query(layer, map_scaling, raw_gpx_data) + map_centers = create_map_centers(map_scaling, raw_gpx_data) - base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) - url = '{}/print/default/report.pdf'.format(base_url) - response_obj = requests.post(url, data=json.dumps(query_json)) - if response_obj.status_code != 200: - raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + if len(map_centers) > 10: + raise Exception("You should respect the faire use limit!") + return - response_json = json.loads(response_obj.content) + for index, map_center in enumerate(map_centers): + + query_json = create_mapfish_query(layer, map_scaling, raw_gpx_data, map_center) + + base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) + url = '{}/print/default/report.pdf'.format(base_url) + response_obj = requests.post(url, data=json.dumps(query_json)) + + if response_obj.status_code != 200: + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + + response_json = json.loads(response_obj.content) - pdf_status = requests.get(base_url + response_json['statusURL']) - while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': - time.sleep(0.5) pdf_status = requests.get(base_url + response_json['statusURL']) - print(json.loads(pdf_status.content)['status']) + while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': + time.sleep(0.5) + pdf_status = requests.get(base_url + response_json['statusURL']) + print(json.loads(pdf_status.content)['status']) - if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': - raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) - fetched_pdf = requests.get(base_url + response_json['downloadURL']) + fetched_pdf = requests.get(base_url + response_json['downloadURL']) - if response_obj.status_code != 200: - raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) + if response_obj.status_code != 200: + raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) - with open('output/{}_map.pdf'.format(file_name), 'wb') as f: - f.write(fetched_pdf.content) + with open('output/{}_{}_map.pdf'.format(file_name, index), 'wb') as f: + f.write(fetched_pdf.content) -def create_mapfish_query(layer, map_scaling, raw_gpx_data): +def create_mapfish_query(layer, map_scaling, raw_gpx_data, center): """ Returns the JSON-Object used for querying @@ -78,7 +91,7 @@ def create_mapfish_query(layer, map_scaling, raw_gpx_data): "outputFormat": "pdf", "attributes": { "map": { - "center": path_coordinates[0], + "center": center, "scale": map_scaling, "dpi": 400, "pdfA": True, @@ -144,9 +157,77 @@ def create_mapfish_query(layer, map_scaling, raw_gpx_data): } } } + + return query_json +def GetSpacedElements(array, numElems=4): + + """ + Get numElems of an array, that are equally spaced based on index (not value). + """ + + indices = np.round(np.linspace(0, len(array) - 1, numElems)).astype(int) + return list(np.array(array)[indices]) + + +def create_map_centers(map_scaling: int, raw_gpx_data: gpxpy.gpx) -> []: + """ + + Calculates the map centers based on the approach discussed here: + https://stackoverflow.com/questions/51946065/cover-a-polygonal-line-using-the-least-given-rectangles-while-keeping-her-contin + + """ + + points = get_path_coordinates_as_list(raw_gpx_data) + + w = 6.5 / 25.0 * map_scaling + h = 4.5 / 25.0 * map_scaling + n = 0 + n_points = 200 + + path_covered = False + + while not path_covered: + + n = n + 1 + points_for_clustering = GetSpacedElements(points, n_points) + + user_function = lambda point1, point2: max(abs(point1[0] - point2[0]) / (w / 2), + abs(point1[1] - point2[1]) / (h / 2)) + metric = distance_metric(type_metric.USER_DEFINED, func=user_function) + + # create K-Means algorithm with specific distance metric + start_centers = GetSpacedElements(points_for_clustering, n) + kmeans_instance = kmeans(points_for_clustering, start_centers, metric=metric) + + # Run cluster analysis and obtain results. + kmeans_instance.process() + clusters = kmeans_instance.get_clusters() + final_centers = kmeans_instance.get_centers() + + path_covered = True + + for i, pkt in enumerate(points): + + point_covered = False + for center in final_centers: + d = user_function(center, pkt) + if d < 1: + point_covered = True + break + + if not point_covered: + path_covered = False + break + + if n >= 25: + path_covered = True + + return final_centers + + def get_path_coordinates_as_list(raw_gpx_data): path_coordinates = [] converter = coord_transformation.GPSConverter() From 320c492106013107589f5b516144d0201393bd5b Mon Sep 17 00:00:00 2001 From: Matthias Busenhart Date: Mon, 25 Oct 2021 21:52:21 +0200 Subject: [PATCH 10/18] Fix import and add pyclustering to requirements --- python_program/automatic_walk_time_tables/requirements.txt | 1 + python_program/main.py | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/python_program/automatic_walk_time_tables/requirements.txt b/python_program/automatic_walk_time_tables/requirements.txt index 48a4cf27..03c22a50 100644 --- a/python_program/automatic_walk_time_tables/requirements.txt +++ b/python_program/automatic_walk_time_tables/requirements.txt @@ -6,3 +6,4 @@ openpyxl==3.0.7 matplotlib==3.4.3 grequests==0.6.0 argparse==1.4.0 +pyclustering==0.10.1.2 \ No newline at end of file diff --git a/python_program/main.py b/python_program/main.py index 126f335b..86de0c85 100644 --- a/python_program/main.py +++ b/python_program/main.py @@ -6,7 +6,7 @@ from automatic_walk_time_tables.find_walk_table_points import select_waypoints from automatic_walk_time_tables.map_numbers import find_map_numbers from automatic_walk_time_tables.walk_table import plot_elevation_profile, create_walk_table -from python_program.automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map +from automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_figure: bool, map_scaling: int, From fe027356e99cfaaece8dd9f590e48ac171578fba Mon Sep 17 00:00:00 2001 From: Matthias Busenhart Date: Mon, 25 Oct 2021 22:02:34 +0200 Subject: [PATCH 11/18] Improve print message with time --- .../automatic_walk_time_tables/map_downloader/create_map.py | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index b4a34a02..6fe8af8b 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -56,10 +56,14 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, response_json = json.loads(response_obj.content) pdf_status = requests.get(base_url + response_json['statusURL']) + loop_idx = 0 while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': time.sleep(0.5) pdf_status = requests.get(base_url + response_json['statusURL']) - print(json.loads(pdf_status.content)['status']) + print(f"Waiting for PDF {index+1} out of {len(map_centers)}. ({loop_idx * 0.5}s)", end="\r") + loop_idx += 1 + print() + print(f"Received PDF {index+1} out of {len(map_centers)}.") if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) From 244487bba2f89231a2c43b198f91aea0220f694e Mon Sep 17 00:00:00 2001 From: Matthias Busenhart Date: Mon, 25 Oct 2021 22:14:00 +0200 Subject: [PATCH 12/18] Fix typing, add subsampling to map --- .../map_downloader/create_map.py | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index 6fe8af8b..c86884f4 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -8,12 +8,12 @@ from pathlib import Path from pyclustering.cluster.kmeans import kmeans, kmeans_visualizer from pyclustering.utils.metric import type_metric, distance_metric +from typing import List, Tuple from .. import coord_transformation - def plot_route_on_map(raw_gpx_data: gpxpy.gpx, - way_points: [], + way_points: List[Tuple[int, gpxpy.gpx.GPXTrackPoint]], file_name: str, open_figure: bool, map_scaling: int, @@ -25,7 +25,7 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, Creates a map of the route and marking the selected way points on it. - raw_gpx_data : raw data form imported GPX file + raw_gpx_data : raw data from imported GPX file way_points : selected way points of the walk-time table tile_format_ext : Format of the tile, allowed values jpeg or png, default jpeg layer : Map layer, see https://wmts.geo.admin.ch/EPSG/2056/1.0.0/WMTSCapabilities.xml for options @@ -35,7 +35,13 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, """ - map_centers = create_map_centers(map_scaling, raw_gpx_data) + # Subsample the tracks with the Ramer-Douglas-Peucker algorithm. + subsampled_gpx_data = raw_gpx_data.clone() + for track in subsampled_gpx_data.tracks: + track.simplify() + + + map_centers = create_map_centers(map_scaling, subsampled_gpx_data) if len(map_centers) > 10: @@ -44,7 +50,7 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, for index, map_center in enumerate(map_centers): - query_json = create_mapfish_query(layer, map_scaling, raw_gpx_data, map_center) + query_json = create_mapfish_query(layer, map_scaling, subsampled_gpx_data, map_center) base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) url = '{}/print/default/report.pdf'.format(base_url) @@ -176,7 +182,7 @@ def GetSpacedElements(array, numElems=4): return list(np.array(array)[indices]) -def create_map_centers(map_scaling: int, raw_gpx_data: gpxpy.gpx) -> []: +def create_map_centers(map_scaling: int, raw_gpx_data: gpxpy.gpx) -> List[np.array]: """ Calculates the map centers based on the approach discussed here: From b25a10f624aba789884e35fbd88c7372bf0e3d51 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Tue, 26 Oct 2021 17:17:00 +0200 Subject: [PATCH 13/18] automatically select map scale if not defined --- python_program/Readme.md | 4 +- .../map_downloader/create_map.py | 69 +++++++++++++++---- python_program/main.py | 2 +- 3 files changed, 58 insertions(+), 17 deletions(-) diff --git a/python_program/Readme.md b/python_program/Readme.md index f884d529..8b265c8b 100644 --- a/python_program/Readme.md +++ b/python_program/Readme.md @@ -17,8 +17,8 @@ Name | arguments | Description | | `--gpx-file-name` | `String` | Name and path to the GPX file, if not specified ```./GPX/Default_Route.gpx``` will be used as default value. `--velocity` | `Float` | Speed in km/h on which the calculation is based, default 3.75 km/h. -`--map-scaling` | `Integer` | Scaling of the created map (e.g. 10000 for scaling of 1:10'000), if not specified the scaling will be automatically chosen. -`--open-images` | None | If this flag is set, the created images will be shown (i.g. the map and elevation plot will be opened after its creation). For this feature a desktop environment is needed. +`--map-scaling` | `Integer` | Scaling of the created map (e.g. 10000 for scaling of 1:10'000). If not specified, the scaling will be automatically chosen such that the path can be printed onto a single A4 paper. The scaling gets chosen out of a list of common map scaling: 1:10'000, 1:25'000, 1:50'000, 1:100'000, or 1:200'000. +`--open-images` | None | If this flag is set, the created images will be shown (i.g. elevation plot will be opened after its creation). For this feature a desktop environment is needed. `--departure-time` | ISO-timestamp | Departure date in ISO-format, i.g. 2011-11-04T00:05:23. Default 2021-08-16T09:00:00. `--creator-name` | `String` | The name of the creator of this walk-table. Default is just an empty string. diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index c86884f4..6f31ae83 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -1,17 +1,59 @@ -import gpxpy import json -import matplotlib.pyplot as plt -import numpy as np -import requests import time -from matplotlib.patches import Rectangle from pathlib import Path -from pyclustering.cluster.kmeans import kmeans, kmeans_visualizer -from pyclustering.utils.metric import type_metric, distance_metric from typing import List, Tuple +import gpxpy +import numpy as np +import requests +from pyclustering.cluster.kmeans import kmeans +from pyclustering.utils.metric import type_metric, distance_metric + from .. import coord_transformation +A4_HEIGHT_FACTOR = 4.5 / 25.0 +""" +Used to calculate the size of the map printed on A4 at certain scale: +`A4_HEIGHT_FACTOR * map_scale` gives you the number of km displayed on one A4 paper. +""" + +A4_WIDTH_FACTOR = 6.5 / 25.0 +""" +Used to calculate the size of the map printed on A4 at certain scale: +`A4_WIDTH_FACTOR * map_scale` gives you the number of km displayed on one A4 paper. +""" + + +def auto_select_map_scaling(gpx_data: gpxpy.gpx) -> int: + """ + + Automatically selects a suitable map scaling such that the path can be printed + onto a single A4 paper. While keeping the scaling is as small as possible. The + scaling gets chosen out of a list of common map scaling: 1:10'000, 1:25'000, + 1:50'000, 1:100'000, or 1:200'000. + + gpx_data: the GPX data containing the route information + + """ + + converter = coord_transformation.GPSConverter() + bounds = gpx_data.get_bounds() + + upper_right = converter.WGS84toLV03(bounds.max_latitude, bounds.max_longitude, 0) + lower_left = converter.WGS84toLV03(bounds.min_latitude, bounds.min_longitude, 0) + + # List of most common map scales + common_map_scales = [10_000, 25_000, 50_000, 100_000, 200_000] + + for map_scale in common_map_scales: + if A4_HEIGHT_FACTOR * map_scale >= upper_right[1] - lower_left[1] and \ + A4_WIDTH_FACTOR * map_scale >= lower_left[1] - upper_right[0]: + break + + print(f'Map scaling automatically set to 1:{map_scale}') + return map_scale + + def plot_route_on_map(raw_gpx_data: gpxpy.gpx, way_points: List[Tuple[int, gpxpy.gpx.GPXTrackPoint]], file_name: str, @@ -40,10 +82,11 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, for track in subsampled_gpx_data.tracks: track.simplify() + if map_scaling is None: + map_scaling = auto_select_map_scaling(subsampled_gpx_data) map_centers = create_map_centers(map_scaling, subsampled_gpx_data) - if len(map_centers) > 10: raise Exception("You should respect the faire use limit!") return @@ -66,10 +109,10 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, while pdf_status.status_code == 200 and json.loads(pdf_status.content)['status'] == 'running': time.sleep(0.5) pdf_status = requests.get(base_url + response_json['statusURL']) - print(f"Waiting for PDF {index+1} out of {len(map_centers)}. ({loop_idx * 0.5}s)", end="\r") + print(f"Waiting for PDF {index + 1} out of {len(map_centers)}. ({loop_idx * 0.5}s)", end="\r") loop_idx += 1 print() - print(f"Received PDF {index+1} out of {len(map_centers)}.") + print(f"Received PDF {index + 1} out of {len(map_centers)}.") if response_obj.status_code != 200 and json.loads(pdf_status.content)['status'] != 'finished': raise Exception('Can not fetch map. Status Code: {}'.format(response_obj.status_code)) @@ -168,12 +211,10 @@ def create_mapfish_query(layer, map_scaling, raw_gpx_data, center): } } - return query_json def GetSpacedElements(array, numElems=4): - """ Get numElems of an array, that are equally spaced based on index (not value). """ @@ -192,8 +233,8 @@ def create_map_centers(map_scaling: int, raw_gpx_data: gpxpy.gpx) -> List[np.arr points = get_path_coordinates_as_list(raw_gpx_data) - w = 6.5 / 25.0 * map_scaling - h = 4.5 / 25.0 * map_scaling + w = A4_WIDTH_FACTOR * map_scaling + h = A4_HEIGHT_FACTOR * map_scaling n = 0 n_points = 200 diff --git a/python_program/main.py b/python_program/main.py index 86de0c85..1e419777 100644 --- a/python_program/main.py +++ b/python_program/main.py @@ -39,7 +39,7 @@ def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_ 'default value.', default='./GPX/Default_Route.gpx') parser.add_argument('--velocity', type=float, default=3.75, help='Float. Speed in km/h on which the calculation is based, default 3.75 km/h.') - parser.add_argument('--map-scaling', type=int, default=25_000, + parser.add_argument('--map-scaling', type=int, help='Integer. Scaling of the created map (e.g. 10000 for scaling of 1:10\'000), if not ' 'specified the scaling will be automatically chosen.') parser.add_argument('--open-figure', default=False, action='store_true', From 5c73e41f8330dd2ca08596b249c512f7625a33d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Tue, 2 Nov 2021 15:50:07 +0100 Subject: [PATCH 14/18] add waypoints to map --- .../map_downloader/create_map.py | 201 ++++++++++++------ .../automatic_walk_time_tables/walk_table.py | 4 + python_program/main.py | 4 +- 3 files changed, 142 insertions(+), 67 deletions(-) diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index 6f31ae83..06c60750 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -57,8 +57,8 @@ def auto_select_map_scaling(gpx_data: gpxpy.gpx) -> int: def plot_route_on_map(raw_gpx_data: gpxpy.gpx, way_points: List[Tuple[int, gpxpy.gpx.GPXTrackPoint]], file_name: str, - open_figure: bool, map_scaling: int, + name_of_points: List[str], layer: str = 'ch.swisstopo.pixelkarte-farbe', print_api_base_url: str = 'localhost', print_api_port: int = 8080, @@ -77,15 +77,14 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, """ - # Subsample the tracks with the Ramer-Douglas-Peucker algorithm. + if map_scaling is None: + map_scaling = auto_select_map_scaling(raw_gpx_data) + subsampled_gpx_data = raw_gpx_data.clone() for track in subsampled_gpx_data.tracks: track.simplify() - if map_scaling is None: - map_scaling = auto_select_map_scaling(subsampled_gpx_data) - - map_centers = create_map_centers(map_scaling, subsampled_gpx_data) + map_centers = create_map_centers(map_scaling, raw_gpx_data) if len(map_centers) > 10: raise Exception("You should respect the faire use limit!") @@ -93,7 +92,7 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, for index, map_center in enumerate(map_centers): - query_json = create_mapfish_query(layer, map_scaling, subsampled_gpx_data, map_center) + query_json = create_mapfish_query(layer, map_scaling, raw_gpx_data, map_center, way_points, name_of_points) base_url = "{}://{}:{}".format(print_api_protocol, print_api_base_url, print_api_port) url = '{}/print/default/report.pdf'.format(base_url) @@ -126,7 +125,9 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, f.write(fetched_pdf.content) -def create_mapfish_query(layer, map_scaling, raw_gpx_data, center): +def create_mapfish_query(layer, map_scaling, raw_gpx_data, center, + way_points: List[Tuple[int, gpxpy.gpx.GPXTrackPoint]], + name_of_points): """ Returns the JSON-Object used for querying @@ -139,6 +140,132 @@ def create_mapfish_query(layer, map_scaling, raw_gpx_data, center): with open(str(Path(__file__).resolve().parent) + '/default_map_matrices.json') as json_file: default_matrices = json.load(json_file) + path_layer = { + "geoJson": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "LineString", + "coordinates": path_coordinates + }, + "properties": { + "_ngeo_style": "1,2" + }, + "id": 7772936 + } + ] + }, + "opacity": 1, + "style": { + "version": 2, + "[_ngeo_style = '1,2']": { + "symbolizers": [ + { + "type": "line", + "strokeColor": "#E88615", + "strokeOpacity": 0.5, + "strokeWidth": 2.5 + }, + { + "type": "line", + "strokeColor": "#E88615", + "strokeOpacity": 0.75, + "strokeWidth": .5 + } + ] + } + }, + "type": "geojson", + "name": "selected track" + } + map_layer = { + "baseURL": "https://wmts100.geo.admin.ch/1.0.0/{Layer}/{style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.jpeg", + "dimensions": ["Time"], + "dimensionParams": {"Time": "current"}, + "name": layer, + "imageFormat": "image/jpeg", + "layer": layer, + "matrixSet": "2056", + "opacity": 0.85, + "requestEncoding": "REST", + "matrices": default_matrices, + "style": "default", + "type": "WMTS", + "version": "1.0.0" + } + + point_layers = [] + for i, point in enumerate(way_points): + converter = coord_transformation.GPSConverter() + wgs84 = [point[1].latitude, point[1].longitude, point[1].elevation] + lv03 = converter.WGS84toLV03(wgs84[0], wgs84[1], wgs84[2]) + lv03 = np.round(lv03) + + point_layer = { + "geoJson": { + "type": "FeatureCollection", + "features": [ + { + "type": "Feature", + "geometry": { + "type": "Point", + "coordinates": [lv03[0] + 2_000_000, lv03[1] + 1_000_000] + }, + "properties": { + "_ngeo_style": "1" + }, + "id": 1596274 + } + ] + }, + "opacity": 1, + "style": { + "version": 2, + "[_ngeo_style = '1']": { + "symbolizers": [ + { + "type": "point", + "fillColor": "#FF0000", + "fillOpacity": 0, + "rotation": "30", + + "graphicName": "circle", + "graphicOpacity": 0.4, + "pointRadius": 5, + + "strokeColor": "#e30613", + "strokeOpacity": 1, + "strokeWidth": 2, + "strokeLinecap": "round", + "strokeLinejoin": "round", + }, + { + "type": "text", + "fontColor": "#e30613", + "fontFamily": "sans-serif", + "fontSize": "8px", + "fontStyle": "normal", + "haloColor": "#ffffff", + "haloOpacity": "0.5", + "haloRadius": ".5", + "label": name_of_points[i], + "fillColor": "#FF0000", + "fillOpacity": 0, + "labelAlign": "cm", + "labelRotation": "0", + "labelXOffset": "0", + "labelYOffset": "-12" + } + ] + } + }, + "type": "geojson", + "name": "selected track pois" + } + point_layers.append(point_layer) + query_json = { "layout": "A4 landscape", "outputFormat": "pdf", @@ -150,63 +277,7 @@ def create_mapfish_query(layer, map_scaling, raw_gpx_data, center): "pdfA": True, "projection": "EPSG:2056", "rotation": 0, - "layers": [ - { - "geoJson": { - "type": "FeatureCollection", - "features": [ - { - "type": "Feature", - "geometry": { - "type": "LineString", - "coordinates": path_coordinates - }, - "properties": { - "_ngeo_style": "1,2" - }, - "id": 7772936 - } - ] - }, - "opacity": 1, - "style": { - "version": 2, - "[_ngeo_style = '1,2']": { - "symbolizers": [ - { - "type": "line", - "strokeColor": "#e30613", - "strokeOpacity": 0.5, - "strokeWidth": 2.5 - }, - { - "type": "line", - "strokeColor": "#e30613", - "strokeOpacity": 0.75, - "strokeWidth": .5 - } - ] - } - }, - "type": "geojson", - "name": "selected track" - }, - { - "baseURL": "https://wmts100.geo.admin.ch/1.0.0/{Layer}/{style}/{Time}/{TileMatrixSet}/{TileMatrix}/{TileCol}/{TileRow}.jpeg", - "dimensions": ["Time"], - "dimensionParams": {"Time": "current"}, - "name": layer, - "imageFormat": "image/jpeg", - "layer": layer, - "matrixSet": "2056", - "opacity": 0.85, - "requestEncoding": "REST", - "matrices": default_matrices, - "style": "default", - "type": "WMTS", - "version": "1.0.0" - } - ] + "layers": point_layers + [path_layer, map_layer] } } } diff --git a/python_program/automatic_walk_time_tables/walk_table.py b/python_program/automatic_walk_time_tables/walk_table.py index c996293c..c5bc5511 100644 --- a/python_program/automatic_walk_time_tables/walk_table.py +++ b/python_program/automatic_walk_time_tables/walk_table.py @@ -85,6 +85,8 @@ def create_walk_table(time_stamp, speed, way_points, total_distance, file_name: sheet['N3'] = speed sheet['K8'] = time_stamp.strftime('%H:%M') + name_of_points = [] + # get Infos points for i, point in enumerate(way_points): @@ -104,6 +106,7 @@ def create_walk_table(time_stamp, speed, way_points, total_distance, file_name: # print infos name_of_point = find_swisstopo_name.find_name((lv03[0] + 2_000_000, lv03[1] + 1_000_000), 50) + name_of_points.append(name_of_point) print( round(abs((oldPoint[0] if oldPoint is not None else 0.0) - point[0]), 1), 'km ', int(lv03[2]), 'm ü. M. ', @@ -130,6 +133,7 @@ def create_walk_table(time_stamp, speed, way_points, total_distance, file_name: os.mkdir('output') xfile.save('output/' + file_name + '_Marschzeittabelle.xlsx') + return name_of_points def calcTime(delta_height, delta_dist, speed): diff --git a/python_program/main.py b/python_program/main.py index 1e419777..3fcef305 100644 --- a/python_program/main.py +++ b/python_program/main.py @@ -23,9 +23,9 @@ def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_ # calc Points for walk table total_distance, temp_points, way_points = select_waypoints(raw_gpx_data) plot_elevation_profile(raw_gpx_data, way_points, temp_points, file_name=name, open_figure=open_figure) - create_walk_table(departure_date, velocity, way_points, total_distance, file_name=name, creator_name=creator_name, + name_of_points = create_walk_table(departure_date, velocity, way_points, total_distance, file_name=name, creator_name=creator_name, map_numbers=map_numbers) - plot_route_on_map(raw_gpx_data, way_points, file_name=name, open_figure=open_figure, map_scaling=map_scaling) + plot_route_on_map(raw_gpx_data, way_points, file_name=name, map_scaling=map_scaling, name_of_points=name_of_points) if __name__ == "__main__": From 7f400801a4dbcef0d890bbf1eeea24f12839e1f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Thu, 11 Nov 2021 16:45:12 +0100 Subject: [PATCH 15/18] update documentation and renaming pdf_map_export folder --- Readme.md | 13 +++++-- pdf-map-export/README.md | 20 ---------- pdf_map_export/README.md | 21 +++++++++++ .../cevi_logo.svg | 0 .../config.yaml | 0 .../north_arrow.png | Bin .../report.jrxml | 0 .../test_query.json | 0 python_program/Readme.md | 35 ++++++++++++++---- 9 files changed, 57 insertions(+), 32 deletions(-) delete mode 100644 pdf-map-export/README.md create mode 100644 pdf_map_export/README.md rename {pdf-map-export => pdf_map_export}/cevi_logo.svg (100%) rename {pdf-map-export => pdf_map_export}/config.yaml (100%) rename {pdf-map-export => pdf_map_export}/north_arrow.png (100%) rename {pdf-map-export => pdf_map_export}/report.jrxml (100%) rename pdf-map-export/query.json => pdf_map_export/test_query.json (100%) diff --git a/Readme.md b/Readme.md index d1ffecbe..4c807036 100644 --- a/Readme.md +++ b/Readme.md @@ -8,10 +8,15 @@ Ziel dieses Projektes ist es, den Prozess rund um das Erstellen einer J+S-Marsch Velo-Tour zu automatisieren und zu beschleunigen. Heute gibt es bereits verschiedenste Online-Tool, die J+S-Leiter*innen und Wanderfreudigen das Planen einer Wanderung -erleichtern. Ich selber verwende meistens die kostenpflichtige Online-Karte von SchweizMobil. SchweizMobil ist Dank den -magnetischen Wegen (d.h. die eingezeichnete Route folgt automatisch dem Wanderweg/der Strasse) und der Zeitberechnung ( -gemäss der Formel der Schweizer Wanderwege) bereits eine grosse Hilfe beim Planen. Ebenfalls geeignet ist die offizielle -App des Bundesamts für Landestopografie swisstopo. +erleichtern: + +- Die kostenpflichtige Online-Karte von SchweizMobil. SchweizMobil ist Dank den magnetischen Wegen (d.h. die +eingezeichnete Route folgt automatisch dem Wanderweg/der Strasse) und der Zeitberechnung (gemäss der Formel der +Schweizer Wanderwege) bereits eine grosse Hilfe beim Planen. + +- Für mobile Endgeräte, eignet sich ebenfalls die offizielle App des Bundesamts für Landestopografie swisstopo. +Analog zur SchweizMobil, bietet auch die swisstopo App eine Funktion zur Routenplanung mit magnetischen Wegen. + Ist man mit einer grösseren Gruppe unterwegs (so zum Beispiel in einem J+S-Lager), ist eine ausführliche Planung unumgänglich. Doch genau in diesen Szenarien stossen die existierenden Tools an ihr Grenzen. Dieses Projekt setzt genau diff --git a/pdf-map-export/README.md b/pdf-map-export/README.md deleted file mode 100644 index 2bc9d468..00000000 --- a/pdf-map-export/README.md +++ /dev/null @@ -1,20 +0,0 @@ -# Lokal MapFish Export - - - -## 1) Run docker: - -`docker run --name=mapfish-print-test --mount type=bind,source=/mnt/d/Projekte/Marschzeittabelle/pdf-map-export,target=/usr/local/tomcat/webapps/ROOT/print-apps/swisstopo --publish=8080:8080 camptocamp/mapfish_print` - - - -## 2) Open localhost:8080 - -Copy `query.json` to input field and print a PDF - - - -## Some notes about the query - -The query uses "projection": "EPSG:2056", i.g. it uses the LV95 coordinate format. Do not modify the "matrices" attribute! - diff --git a/pdf_map_export/README.md b/pdf_map_export/README.md new file mode 100644 index 00000000..83b4e092 --- /dev/null +++ b/pdf_map_export/README.md @@ -0,0 +1,21 @@ +# Docker Container running MapFish +As of version 1.4.0, the script is divided into two main components: + +- A python script executed directly by the end user (entry-point `../python_program/main.py`) +- A docker container running a MapFish instance used to create PDF-map exports via a web api. + +## Run MapFish inside a Local Docker Container: +MapFish is an open-source tool to create reports containing maps. This script uses MapFish to create the PDF-map +exports. We relay directly on the default `camptocamp/mapfish_print` docker container, customized by binding of our +settings. + +Once you have Docker installed on your system, you can start the MapFish print server by calling: + + docker run --name=mapfish-print-test \ + --mount type=bind,source=,target=/usr/local/tomcat/webapps/ROOT/print-apps/swisstopo \ + --publish=8080:8080 camptocamp/mapfish_print + +## Test your MapFish Instance +You can test if the container is running properly by opening [http://localhost:8080](http://localhost:8080/). Now you +can copy-past the content of the `test_query.json` file and click on `Create And Get Print`. If the test PDF gets +created properly, your installation was successfully. diff --git a/pdf-map-export/cevi_logo.svg b/pdf_map_export/cevi_logo.svg similarity index 100% rename from pdf-map-export/cevi_logo.svg rename to pdf_map_export/cevi_logo.svg diff --git a/pdf-map-export/config.yaml b/pdf_map_export/config.yaml similarity index 100% rename from pdf-map-export/config.yaml rename to pdf_map_export/config.yaml diff --git a/pdf-map-export/north_arrow.png b/pdf_map_export/north_arrow.png similarity index 100% rename from pdf-map-export/north_arrow.png rename to pdf_map_export/north_arrow.png diff --git a/pdf-map-export/report.jrxml b/pdf_map_export/report.jrxml similarity index 100% rename from pdf-map-export/report.jrxml rename to pdf_map_export/report.jrxml diff --git a/pdf-map-export/query.json b/pdf_map_export/test_query.json similarity index 100% rename from pdf-map-export/query.json rename to pdf_map_export/test_query.json diff --git a/python_program/Readme.md b/python_program/Readme.md index 8b265c8b..d76e5458 100644 --- a/python_program/Readme.md +++ b/python_program/Readme.md @@ -1,14 +1,33 @@ -# How to run the script? +## How to run the script? -Make sure you have installed python 3 and all requirements listed in the requirements.txt file. Now you can -run ```main.py``` to launch the script. The produced files get saved in the ```./output``` directory. In -the ```main.py``` you can specify the ```DEPARTURE_TIME```, ```GPX_FILE_PATH```, and ```VELOCITY``` as command-line -arguments, see table bellow. +As of version 1.4.0, the script is divided into two main components: -Note: the script is only tested with GPX-files exported form SchweizMobil and from the official swisstopo app, but it -should work with arbitrary GPX-files. +- A python script executed directly by the end user (entry-point `./main.py`) +- A docker container running a MapFish instance used to create PDF-map exports via a web api (see `../pdf_map_export` + folder). -## Settings: Supported command-line args +## Prerequisites + +1) Make sure you have installed python 3 and all requirements listed in + the `./automatic_walk_time_tables/requirements.txt` file. You can use the following command to install the + dependencies: + + pip3 install -r ./automatic_walk_time_tables/requirements.txt + +2) In order to create PDF-map exports, you need to set up a docker container. Please follow + the [instructions](../pdf_map_export/README.md) inside the `../pdf_map_export` folder. + +## Launch the script + +You can launch the script by calling: + + main.py --gpx-file-name + +Where the `gpx-file-name` flag specifies the path to your GPX file. Once the script has terminated, the produced files ( +Excel, PDFs...) are stored in the ```./output``` directory. Note: the script is only tested with GPX-files exported form +SchweizMobil and from the official swisstopo app, but it should work with arbitrary GPX-files. + +### Additional Settings: Supported command-line args All arguments are optional (or have a default value). However, the arguments allow choosing various settings. From 45c9b3121491735c092a72db7553220cb0c6acf7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Thu, 11 Nov 2021 17:07:02 +0100 Subject: [PATCH 16/18] add missing types --- .../automatic_walk_time_tables/map_downloader/create_map.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_program/automatic_walk_time_tables/map_downloader/create_map.py b/python_program/automatic_walk_time_tables/map_downloader/create_map.py index 06c60750..1b8d332b 100644 --- a/python_program/automatic_walk_time_tables/map_downloader/create_map.py +++ b/python_program/automatic_walk_time_tables/map_downloader/create_map.py @@ -125,7 +125,7 @@ def plot_route_on_map(raw_gpx_data: gpxpy.gpx, f.write(fetched_pdf.content) -def create_mapfish_query(layer, map_scaling, raw_gpx_data, center, +def create_mapfish_query(layer, map_scaling, raw_gpx_data: gpxpy.gpx, center, way_points: List[Tuple[int, gpxpy.gpx.GPXTrackPoint]], name_of_points): """ @@ -350,7 +350,7 @@ def create_map_centers(map_scaling: int, raw_gpx_data: gpxpy.gpx) -> List[np.arr return final_centers -def get_path_coordinates_as_list(raw_gpx_data): +def get_path_coordinates_as_list(raw_gpx_data: gpxpy.gpx): path_coordinates = [] converter = coord_transformation.GPSConverter() for track in raw_gpx_data.tracks: From 1f989540ef89fcc18bf46fefe7ada87d8364104c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Cyrill=20P=C3=BCntener?= Date: Mon, 15 Nov 2021 14:38:49 +0100 Subject: [PATCH 17/18] [#17] add command-line options for enable/disable features --- python_program/Readme.md | 31 +++++++++----- python_program/main.py | 87 ++++++++++++++++++++++++++-------------- 2 files changed, 79 insertions(+), 39 deletions(-) diff --git a/python_program/Readme.md b/python_program/Readme.md index d76e5458..67e5dfe6 100644 --- a/python_program/Readme.md +++ b/python_program/Readme.md @@ -15,7 +15,8 @@ As of version 1.4.0, the script is divided into two main components: pip3 install -r ./automatic_walk_time_tables/requirements.txt 2) In order to create PDF-map exports, you need to set up a docker container. Please follow - the [instructions](../pdf_map_export/README.md) inside the `../pdf_map_export` folder. + the [instructions](../pdf_map_export/README.md) inside the `../pdf_map_export` folder. You can disable the PDF export + feature with the `--create-map-pdfs False` argument. ## Launch the script @@ -27,19 +28,31 @@ Where the `gpx-file-name` flag specifies the path to your GPX file. Once the scr Excel, PDFs...) are stored in the ```./output``` directory. Note: the script is only tested with GPX-files exported form SchweizMobil and from the official swisstopo app, but it should work with arbitrary GPX-files. -### Additional Settings: Supported command-line args +## Additional Settings via Command-Line Arguments All arguments are optional (or have a default value). However, the arguments allow choosing various settings. Name | arguments | Description ---- | --- | --- -| | -`--gpx-file-name` | `String` | Name and path to the GPX file, if not specified ```./GPX/Default_Route.gpx``` will be used as default value. -`--velocity` | `Float` | Speed in km/h on which the calculation is based, default 3.75 km/h. -`--map-scaling` | `Integer` | Scaling of the created map (e.g. 10000 for scaling of 1:10'000). If not specified, the scaling will be automatically chosen such that the path can be printed onto a single A4 paper. The scaling gets chosen out of a list of common map scaling: 1:10'000, 1:25'000, 1:50'000, 1:100'000, or 1:200'000. +--- | --- | ------------ +`-gfn` `--gpx-file-name` | `String` | Name and path to the GPX file default value. Required Argument. + +### Optional Arguments + +Name | arguments | Description +--- | --- | ------------ +`-v` `--velocity` | `Float` | Speed in km/h on which the calculation is based, default 3.75 km/h. +`-s` `--map-scaling` | `Integer` | Scaling of the created map (e.g. 10000 for scaling of 1:10'000). If not specified, the scaling will be automatically chosen such that the path can be printed onto a single A4 paper. The scaling gets chosen out of a list of common map scaling: 1:10'000, 1:25'000, 1:50'000, 1:100'000, or 1:200'000. +`-t` `--departure-time` | ISO-timestamp | Departure date in ISO-format, i.g. 2011-11-04T00:05:23. Default 2021-08-16T09:00:00. +`-n` `--creator-name` | `String` | The name of the creator of this walk-table. Default is just an empty string. + +### Enable/Disable Features + +Name | arguments | Description +--- | --- | ------------ +`--create-map-pdfs` | Boolean | Enable/Disable export as PDF. Require a running MapFish docker container. Enabled as default (True). +`--create-excel` | Boolean | Enable/Disable creation of the walk-time table as excel. Enabled as default (True). +`--create-elevation-profile` | Boolean | Enable/Disable creation of the elevation profile as PNG. Enabled as default (True). `--open-images` | None | If this flag is set, the created images will be shown (i.g. elevation plot will be opened after its creation). For this feature a desktop environment is needed. -`--departure-time` | ISO-timestamp | Departure date in ISO-format, i.g. 2011-11-04T00:05:23. Default 2021-08-16T09:00:00. -`--creator-name` | `String` | The name of the creator of this walk-table. Default is just an empty string. ## About swisstopo Services diff --git a/python_program/main.py b/python_program/main.py index 3fcef305..53a882e7 100644 --- a/python_program/main.py +++ b/python_program/main.py @@ -4,58 +4,85 @@ import gpxpy.gpx from automatic_walk_time_tables.find_walk_table_points import select_waypoints +from automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map from automatic_walk_time_tables.map_numbers import find_map_numbers from automatic_walk_time_tables.walk_table import plot_elevation_profile, create_walk_table -from automatic_walk_time_tables.map_downloader.create_map import plot_route_on_map -def generate_automated_walk_table(departure_date, gpx_file_path, velocity, open_figure: bool, map_scaling: int, - creator_name: str): +def generate_automated_walk_table(args: argparse.Namespace): # Open GPX-File with the way-points - gpx_file = open(gpx_file_path, 'r') + gpx_file = open(args.gpx_file_name, 'r') raw_gpx_data = gpxpy.parse(gpx_file) # get Meta-Data name = raw_gpx_data.name - map_numbers = find_map_numbers(raw_gpx_data) - # calc Points for walk table - total_distance, temp_points, way_points = select_waypoints(raw_gpx_data) - plot_elevation_profile(raw_gpx_data, way_points, temp_points, file_name=name, open_figure=open_figure) - name_of_points = create_walk_table(departure_date, velocity, way_points, total_distance, file_name=name, creator_name=creator_name, - map_numbers=map_numbers) - plot_route_on_map(raw_gpx_data, way_points, file_name=name, map_scaling=map_scaling, name_of_points=name_of_points) + print(args) + + if args.create_excel or args.create_map_pdfs or args.create_elevation_profile: + + # calc Points for walk table + total_distance, temp_points, way_points = select_waypoints(raw_gpx_data) + + if args.create_elevation_profile: + print('Create elevation profile.') + plot_elevation_profile(raw_gpx_data, way_points, temp_points, file_name=name, open_figure=args.open_figure) + + if args.create_excel: + print('Create walk-time table as Excel file') + name_of_points = create_walk_table(args.departure_time, args.velocity, way_points, total_distance, + file_name=name, creator_name=args.creator_name, map_numbers=map_numbers) + else: + name_of_points = [''] * len(way_points) + + if args.create_map_pdfs: + print('Create map PDFs') + plot_route_on_map(raw_gpx_data, way_points, file_name=name, map_scaling=args.map_scaling, + name_of_points=name_of_points) + + +def str2bool(v): + if isinstance(v, bool): + return v + if v.lower() in ('yes', 'true', 't', 'y', '1'): + return True + elif v.lower() in ('no', 'false', 'f', 'n', '0'): + return False + else: + raise argparse.ArgumentTypeError('Boolean value expected.') if __name__ == "__main__": # Initialize parser - msg = "Supported command-line args: " - parser = argparse.ArgumentParser(description=msg) + parser = argparse.ArgumentParser( + description='Automatically creates a walk-time table from an exported track from Swisstopo-App, ' + 'SchweizMobil, or form an arbitrary GPX-file') # Adding arguments - parser.add_argument('--gpx-file-name', type=str, - help='Name and path to the GPX file, if not specified ./GPX/Default_Route.gpx will be used as ' - 'default value.', default='./GPX/Default_Route.gpx') - parser.add_argument('--velocity', type=float, default=3.75, + parser.add_argument('-gfn', '--gpx-file-name', type=str, metavar='', required=True, + help='Name and path to the GPX file default value.') + parser.add_argument('-v', '--velocity', type=float, default=3.75, metavar='', help='Float. Speed in km/h on which the calculation is based, default 3.75 km/h.') - parser.add_argument('--map-scaling', type=int, + parser.add_argument('-s', '--map-scaling', type=int, metavar='', help='Integer. Scaling of the created map (e.g. 10000 for scaling of 1:10\'000), if not ' 'specified the scaling will be automatically chosen.') - parser.add_argument('--open-figure', default=False, action='store_true', - help='If this flag is set, the created images will be shown (i.g. the map and elevation plot ' - 'will be opened after its creation). For this feature a desktop environment is needed.') - parser.add_argument('--departure-time', type=lambda s: datetime.fromisoformat(s), + parser.add_argument('-t', '--departure-time', type=lambda s: datetime.fromisoformat(s), metavar='