OpenSeaMap-dev Diskussion:Downloadable Raster Charts: Unterschied zwischen den Versionen
Kannix (Diskussion | Beiträge) (→prerequisites: changed from perl to python) |
Kannix (Diskussion | Beiträge) (inland waterways added) |
||
| (7 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt) | |||
| Zeile 1: | Zeile 1: | ||
| − | == ideas == | + | == inland waterways (ideas) == |
| + | === web frontend === | ||
| + | * http://kap.grade.de/inland-chart-ol2.html | ||
| + | ** prefabricated sets of tiles ([http://wiki.openstreetmap.org/wiki/MBTiles mbtiles] format) | ||
| + | ** [http://developer.mapquest.com/de/web/products/open/map mapquest] base-layer | ||
| + | ** CEMT overlay (waterway and bridge classification) | ||
| + | ** OSeaM seamark overlay | ||
| + | |||
| + | === design (current) === | ||
| + | * only zoom levels 7 - 15 | ||
| + | * only corridor of tiles alongside waterways | ||
| + | * saving bandwidth, tiles not showing a navigable waterway are likely missing (eg. lakes beyond fairways) | ||
| + | |||
| + | === workflow (not yet automated) === | ||
| + | * query agains my EU-CEMT mbtiles-db to get tilenames | ||
| + | * expand tilenames-set to a corridor | ||
| + | * download tiles | ||
| + | * pack tiles into mbtiles-files | ||
| + | * upload mbtiles-files | ||
| + | |||
| + | |||
| + | == bluewater charts (ideas) == | ||
=== web frontend === | === web frontend === | ||
* die INT-chart Grenzen (als Basis fuer ein download-tool) bekomme ich inzwischen auf den Schirm: http://kap.grade.de/int-chart-ol2.html , sicherlich verbesserungsbeduerftig | * die INT-chart Grenzen (als Basis fuer ein download-tool) bekomme ich inzwischen auf den Schirm: http://kap.grade.de/int-chart-ol2.html , sicherlich verbesserungsbeduerftig | ||
| Zeile 24: | Zeile 45: | ||
* cronjob checkt queue-db alle x Minuten | * cronjob checkt queue-db alle x Minuten | ||
* waehlt aeltesten job aus queue-db mit status=open | * waehlt aeltesten job aus queue-db mit status=open | ||
| − | * sinnvolles zoom-level anhand bbox errechnen (max. 10MB/chart) | + | * tiledownload ueber proxy mit landez |
| − | + | ** sinnvolles zoom-level anhand bbox errechnen (max. 10MB/chart) | |
| − | + | ** mbtiles erzeugen (siehe auch [https://github.com/mapbox/mbtiles-spec/wiki/Implementations mbtiles implementation]) | |
| − | + | ** stitchen und .png/.jpg erzeugen | |
| − | + | ** .kap mit imgkap erstellen | |
** ... | ** ... | ||
* status=done, zoom-level, date, ... in queue-db setzen | * status=done, zoom-level, date, ... in queue-db setzen | ||
* temp-dir bereinigen und results in charts-dir kopieren | * temp-dir bereinigen und results in charts-dir kopieren | ||
| + | * erste Ergebnisse meiner Versuche: [http://kap.grade.de/INT-CHART/ kap.grade.de] | ||
==== prerequisites ==== | ==== prerequisites ==== | ||
| Zeile 42: | Zeile 64: | ||
** [https://github.com/makinacorpus/landez landez] | ** [https://github.com/makinacorpus/landez landez] | ||
* [http://www.dacust.com/inlandwaters/imgkap/ imgkap] | * [http://www.dacust.com/inlandwaters/imgkap/ imgkap] | ||
| + | |||
| + | ==== py script ==== | ||
| + | * relies on [https://github.com/makinacorpus/landez landez] | ||
| + | |||
| + | <pre> | ||
| + | |||
| + | import logging | ||
| + | import shutil | ||
| + | import math | ||
| + | from landez import MBTilesBuilder, ImageExporter, TilesManager | ||
| + | import sqlite3 | ||
| + | import os | ||
| + | import sys | ||
| + | import codecs | ||
| + | |||
| + | logging.basicConfig(level=logging.DEBUG) | ||
| + | |||
| + | |||
| + | # to be imported from sqlite-queue | ||
| + | con = sqlite3.connect('queue.sqlite') | ||
| + | |||
| + | with con: | ||
| + | |||
| + | con.row_factory = sqlite3.Row | ||
| + | |||
| + | cur = con.cursor() | ||
| + | cur.execute('SELECT * FROM jobs WHERE status="open" ORDER BY microtime DESC') | ||
| + | |||
| + | rows = cur.fetchall() | ||
| + | |||
| + | if len(rows) == 0: | ||
| + | logging.debug(" Nothing to do, exiting ") | ||
| + | sys.exit(0) | ||
| + | |||
| + | for row in rows: | ||
| + | sqMicrotime = row["Microtime"] | ||
| + | sqChart = row["Chart"] | ||
| + | sqName = row["Name"] | ||
| + | sqHO = row["HO"] | ||
| + | sqBottomGeoPre = row["BottomGeoPre"] | ||
| + | sqTopGeoPre = row["TopGeoPre"] | ||
| + | sqLeftGeoPre = row["LeftGeoPre"] | ||
| + | sqRightGeoPre = row["RightGeoPre"] | ||
| + | sqNumTiles16 = row["NumTiles16"] | ||
| + | |||
| + | # to be calculated from numtiles16, numtiles < 700 | ||
| + | # numtiles16 is right now hardcoded at int-chart.gejson for ol depth of view reasons, there might be smarter ways in future | ||
| + | # https://github.com/makinacorpus/landez/issues/44 | ||
| + | myNumTiles = sqNumTiles16 * 4 * 4 | ||
| + | myZoom = 18 | ||
| + | |||
| + | while (myNumTiles > 700): | ||
| + | myNumTiles = myNumTiles / 4 | ||
| + | myZoom = myZoom - 1 | ||
| + | |||
| + | # settings | ||
| + | # myUrlBase = "http://tiles.grade.de/tiles.py/openriverboatmap/{z}/{x}/{y}.png" | ||
| + | myUrlBase = "http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg" | ||
| + | myUrlSeamark = "http://tiles.grade.de/tiles.py/seamark/{z}/{x}/{y}.png" | ||
| + | |||
| + | if sqHO == "DE": | ||
| + | myUrlHydro = "http://tiles.grade.de/tiles.py/NAUTHIS_SkinOfEarth/{z}/{x}/{y}.png" | ||
| + | |||
| + | elif sqHO== "NL/GB": | ||
| + | myUrlHydro = "http://tiles.grade.de/tiles.py/ncp_diepte14/{z}/{x}/{y}.png" | ||
| + | else: | ||
| + | myUrlHydro = "" | ||
| + | |||
| + | myFileName = str(sqMicrotime) | ||
| + | myMbtilesFile = myFileName + ".mbtiles" | ||
| + | myImageFile = myFileName + ".jpg" | ||
| + | myKapFile = myFileName + ".kap" | ||
| + | myTextFile = myFileName + ".txt" | ||
| + | myBbox = (sqLeftGeoPre, sqBottomGeoPre, sqRightGeoPre, sqTopGeoPre) | ||
| + | myZoomLevels = [myZoom] | ||
| + | myZoomLevel = myZoom | ||
| + | myTileSize = 256 | ||
| + | #myTileFormat = 'image/png' | ||
| + | myTileFormat = 'image/jpeg' | ||
| + | |||
| + | # download and mbtiles build | ||
| + | mb = MBTilesBuilder(tiles_url=myUrlBase, cache=True, filepath=myMbtilesFile, tile_size=myTileSize, tile_format=myTileFormat) | ||
| + | mb.add_coverage(myBbox, myZoomLevels) | ||
| + | |||
| + | if myUrlHydro != "": | ||
| + | hydro = TilesManager(tiles_url=myUrlHydro) | ||
| + | seamark = TilesManager(tiles_url=myUrlSeamark) | ||
| + | hydro.add_layer(seamark) | ||
| + | overlay = hydro | ||
| + | else: | ||
| + | seamark = TilesManager(tiles_url=myUrlSeamark) | ||
| + | overlay = seamark | ||
| + | |||
| + | mb.add_layer(overlay) | ||
| + | mb.run(force=True) | ||
| + | |||
| + | # export image from mbtiles | ||
| + | # not the best idea: causes IO at temp dir, again | ||
| + | ie = ImageExporter(mbtiles_file=myMbtilesFile) | ||
| + | ie.export_image(myBbox, myZoomLevel, imagepath=myImageFile) | ||
| + | # save the geoinfo | ||
| + | myGrid = ie.grid_tiles(myBbox, myZoomLevel) | ||
| + | sqWidth = len(myGrid[0]) | ||
| + | sqHeight = len(myGrid) | ||
| + | sqWidthPix = sqWidth * myTileSize | ||
| + | sqHeightPix = sqHeight * myTileSize | ||
| + | |||
| + | # delete temp dir (ImageExporter does not!) | ||
| + | # bad: path is hard coded. how do I get hold of landez's temp path? | ||
| + | # https://github.com/makinacorpus/landez/issues/42 | ||
| + | try: | ||
| + | shutil.rmtree('/tmp/landez') | ||
| + | except OSError: | ||
| + | pass | ||
| + | |||
| + | # geoinfo output | ||
| + | logging.debug(" grid: " + str(myGrid)) | ||
| + | |||
| + | myLeftColumn = myGrid[0] | ||
| + | myTopLeft = myLeftColumn[0] | ||
| + | sqLeftTile = myTopLeft[0] | ||
| + | sqTopTile = myTopLeft[-1] | ||
| + | |||
| + | myRightColumn = myGrid[-1] | ||
| + | myBottomRight = myRightColumn[-1] | ||
| + | sqRightTile = myBottomRight[0] | ||
| + | sqBottomTile = myBottomRight[-1] | ||
| + | |||
| + | # calculate coords from tilenumbers | ||
| + | def num2deg(xtile, ytile, zoom): | ||
| + | n = 2.0 ** zoom | ||
| + | lon_deg = -180.0 + 360.0 * xtile / n | ||
| + | lat_rad = math.atan(math.sinh(math.pi * (1.0 - 2.0 * (ytile / n)))) | ||
| + | lat_deg = 180.0 * lat_rad / math.pi | ||
| + | return (lon_deg, lat_deg) | ||
| + | |||
| + | sqBottomGeoPost = (num2deg(sqRightTile + 1, sqBottomTile + 1, myZoom)[-1]) | ||
| + | sqTopGeoPost = (num2deg(sqLeftTile, sqTopTile, myZoom)[-1]) | ||
| + | sqLeftGeoPost = (num2deg(sqLeftTile, sqTopTile, myZoom)[0]) | ||
| + | sqRightGeoPost = (num2deg(sqRightTile + 1, sqBottomTile + 1, myZoom)[0]) | ||
| + | |||
| + | # shall be exported to sqlite-queue | ||
| + | logging.debug(" baselayer: " + str(myUrlBase)) | ||
| + | logging.debug(" seamark: " + str(myUrlSeamark)) | ||
| + | logging.debug(" hydro: " + str(myUrlHydro)) | ||
| + | logging.debug(" zoom: " + str(myZoom)) | ||
| + | |||
| + | logging.debug(" width: " + str(sqWidth) + " tiles") | ||
| + | logging.debug(" height: " + str(sqHeight) + " tiles") | ||
| + | logging.debug(" widthpix: " + str(sqWidthPix) + " pixel") | ||
| + | logging.debug(" heightpix: "+ str(sqHeightPix) + " pixel") | ||
| + | |||
| + | logging.debug(" left tile " + str(sqLeftTile)) | ||
| + | logging.debug(" right tile: " + str(sqRightTile)) | ||
| + | logging.debug(" top tile: " + str(sqTopTile)) | ||
| + | logging.debug(" bottom tile: "+ str(sqBottomTile)) | ||
| + | |||
| + | logging.debug(" left: " + str(sqLeftGeoPost)) | ||
| + | logging.debug(" right: " + str(sqRightGeoPost)) | ||
| + | logging.debug(" top: " + str(sqTopGeoPost)) | ||
| + | logging.debug(" bottom: " + str(sqBottomGeoPost)) | ||
| + | |||
| + | sqPost = [ | ||
| + | 'UrlBase="' + str(myUrlBase) + '"', | ||
| + | 'UrlSeamark="' + str(myUrlSeamark) + '"', | ||
| + | 'UrlHydro="' + str(myUrlHydro) + '"', | ||
| + | 'Zoom=' + str(myZoom), | ||
| + | 'BottomGeoPost='+ str(sqBottomGeoPost), | ||
| + | 'TopGeoPost='+ str(sqTopGeoPost), | ||
| + | 'LeftGeoPost='+ str(sqLeftGeoPost), | ||
| + | 'RightGeoPost='+ str(sqRightGeoPost), | ||
| + | 'status="done"' | ||
| + | ] | ||
| + | t = ", " | ||
| + | sqPost = t.join(sqPost) | ||
| + | sqlUpdate = "UPDATE jobs " + "SET " + str(sqPost) + " WHERE Microtime=" + str(sqMicrotime) | ||
| + | logging.debug(" sqlUpdate: " + str(sqlUpdate)) | ||
| + | |||
| + | con = sqlite3.connect('queue.sqlite') | ||
| + | with con: | ||
| + | con.row_factory = sqlite3.Row | ||
| + | cur = con.cursor() | ||
| + | cur.execute(sqlUpdate) | ||
| + | con.commit() | ||
| + | logging.debug(" Number of rows updated: " + str(cur.rowcount)) | ||
| + | |||
| + | # start imgkap to generate .kap file | ||
| + | import subprocess | ||
| + | myImgkapCall = "./imgkap " + myImageFile + " " + str(sqTopGeoPost) + " " + str(sqLeftGeoPost) + " " + str(sqBottomGeoPost) + " " + str(sqRightGeoPost) + " " + myKapFile | ||
| + | print myImgkapCall | ||
| + | subprocess.call(myImgkapCall, shell=True) | ||
| + | |||
| + | # write textfile | ||
| + | txtPost = [ | ||
| + | 'These charts cannot replace official charts, use at your own risk!', | ||
| + | '', | ||
| + | 'INT Chart Number= ' + sqChart, | ||
| + | 'INT Chart Name= ' + sqName, | ||
| + | '', | ||
| + | 'Copyright Notices:', | ||
| + | 'Base Layer= http://developer.mapquest.com/de/web/products/open/map', | ||
| + | 'Seamark Layer= http://www.openseamap.org/', | ||
| + | 'DE Hydrography= http://gdiwiki.bsh.de/wiki/index.php/NAUTHIS_Skin_Of_The_Earth', | ||
| + | 'NL Hydrography= http://www.rijkswaterstaat.nl/zakelijk/databestanden/online_geografische_gegevens_rijkswaterstaat/', | ||
| + | '', | ||
| + | 'Zoom Level= ' + str(myZoom), | ||
| + | '', | ||
| + | 'Bottom= '+ str(sqBottomGeoPost), | ||
| + | 'Top= '+ str(sqTopGeoPost), | ||
| + | 'Left= '+ str(sqLeftGeoPost), | ||
| + | 'Right= '+ str(sqRightGeoPost), | ||
| + | '', | ||
| + | 'width= ' + str(sqWidth) + ' tiles', | ||
| + | 'height= ' + str(sqHeight) + ' tiles', | ||
| + | 'width= ' + str(sqWidthPix) + ' pixel', | ||
| + | 'height= ' + str(sqHeightPix) + ' pixel', | ||
| + | '', | ||
| + | 'left tile number= ' + str(sqLeftTile), | ||
| + | 'right tile number= ' + str(sqRightTile), | ||
| + | 'top tile number= ' + str(sqTopTile), | ||
| + | 'bottom tile number= '+ str(sqBottomTile) | ||
| + | ] | ||
| + | t = "\n" | ||
| + | txtPost = t.join(txtPost) | ||
| + | logging.debug(" txtPost: " + txtPost) | ||
| + | |||
| + | with codecs.open(myTextFile, 'w', "utf-8") as f: | ||
| + | f.write(txtPost) | ||
| + | |||
| + | # move and rename files | ||
| + | myPath = "../INT-CHART/INT-" | ||
| + | myNotice = "_MapQuest-OSeaM-BSH" | ||
| + | |||
| + | myNewMbtilesFile = myPath + sqChart + myNotice + ".mbtiles" | ||
| + | myNewImageFile = myPath + sqChart + myNotice + ".jpg" | ||
| + | myNewKapFile = myPath + sqChart + myNotice + ".kap" | ||
| + | myNewTextFile = myPath + sqChart + myNotice + ".txt" | ||
| + | |||
| + | try: | ||
| + | os.rename (myMbtilesFile, myNewMbtilesFile) | ||
| + | os.remove(myMbtilesFile) | ||
| + | except OSError: | ||
| + | pass | ||
| + | |||
| + | try: | ||
| + | os.rename (myImageFile, myNewImageFile) | ||
| + | os.remove(myImageFile) | ||
| + | except OSError: | ||
| + | pass | ||
| + | |||
| + | try: | ||
| + | os.rename (myKapFile, myNewKapFile) | ||
| + | os.remove(myKapFile) | ||
| + | except OSError: | ||
| + | pass | ||
| + | |||
| + | try: | ||
| + | os.rename (myTextFile, myNewTextFile) | ||
| + | os.remove(myTextFile) | ||
| + | except OSError: | ||
| + | pass | ||
| + | |||
| + | |||
| + | </pre> | ||
| + | |||
| + | |||
| + | ==== php script ==== | ||
| + | <pre> | ||
| + | |||
| + | <?php | ||
| + | |||
| + | error_reporting(E_ALL); | ||
| + | |||
| + | show_array($_GET); | ||
| + | show_array($_POST); | ||
| + | |||
| + | switch ($_POST['stage']) | ||
| + | { | ||
| + | case 'process': | ||
| + | update(); | ||
| + | break; | ||
| + | default : | ||
| + | print_list(); | ||
| + | break; | ||
| + | } | ||
| + | |||
| + | function print_form() | ||
| + | { | ||
| + | echo '<form action="'.$_SERVER['PHP_SELF'].'" method="post">'; | ||
| + | echo " <input type='submit' name='Text' value='Generate new Chart'/>"; | ||
| + | echo " <input type='hidden' name ='stage' value='process' />\n"; | ||
| + | echo " <input type='hidden' name ='Chart' value='" . $_GET['Chart'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='Name' value='" . $_GET['Name'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='HO' value='" . $_GET['HO'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='BottomGeoPre' value='" . $_GET['BottomGeoPre'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='TopGeoPre' value='" . $_GET['TopGeoPre'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='LeftGeoPre' value='" . $_GET['LeftGeoPre'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='RightGeoPre' value='" . $_GET['RightGeoPre'] . "' />\n"; | ||
| + | echo " <input type='hidden' name ='NumTiles16' value='" . $_GET['NumTiles16'] . "' />\n"; | ||
| + | echo "</form>\n"; | ||
| + | } | ||
| + | |||
| + | |||
| + | function print_list() | ||
| + | { | ||
| + | if (is_array(glob("../INT-CHART/INT-".$_GET['Chart']."*.*"))) { | ||
| + | foreach (glob("../INT-CHART/INT-".$_GET['Chart']."*.*") as $filename) { | ||
| + | $size = round(filesize($filename)/1024, 2); | ||
| + | $modified = gmdate ( 'd M Y H:i:s', filectime($filename)); | ||
| + | echo "<a href='".$filename."'>" .$filename. "</a> - Size: " . $size . " kB - Modified: " . $modified . "</br>"; | ||
| + | } | ||
| + | } else { | ||
| + | print_form(); | ||
| + | } | ||
| + | } | ||
| + | |||
| + | function update() | ||
| + | { | ||
| + | echo 'Received your request, processing will take some time. Please be patient and check back in an hour ...'; | ||
| + | $_POST['Name'] = utf8_encode( $_POST['Name'] ); | ||
| + | $db = new SQLite3('../temp/queue.sqlite'); | ||
| + | $db->exec("INSERT INTO jobs ( | ||
| + | Chart, | ||
| + | Name, | ||
| + | HO, | ||
| + | BottomGeoPre, | ||
| + | TopGeoPre, | ||
| + | LeftGeoPre, | ||
| + | RightGeoPre, | ||
| + | NumTiles16, | ||
| + | status | ||
| + | ) VALUES ( | ||
| + | '{$_POST['Chart']}', | ||
| + | '{$_POST['Name']}', | ||
| + | '{$_POST['HO']}', | ||
| + | {$_POST['BottomGeoPre']}, | ||
| + | {$_POST['TopGeoPre']}, | ||
| + | {$_POST['LeftGeoPre']}, | ||
| + | {$_POST['RightGeoPre']}, | ||
| + | {$_POST['NumTiles16']}, | ||
| + | 'open' | ||
| + | )"); | ||
| + | } | ||
| + | |||
| + | // show content of PHP array, i.e. show_array($_POST) | ||
| + | // this is handy to quickly test what data is in an array | ||
| + | function show_array($x) | ||
| + | { | ||
| + | if (!is_array($x)) return; | ||
| + | reset($x); | ||
| + | echo "<br />"; | ||
| + | echo "<b>content of array</b>\n"; | ||
| + | if ($x) | ||
| + | while ($i = each($x)) | ||
| + | echo "<br />", htmlentities($i[0]), " = ", htmlentities($i[1]), "\n"; | ||
| + | echo "<br /><br />\n"; | ||
| + | } | ||
| + | |||
| + | ?> | ||
| + | |||
| + | </pre> | ||
== Reason for ChartBundler == | == Reason for ChartBundler == | ||
Aktuelle Version vom 22. Juni 2015, 13:01 Uhr
Inhaltsverzeichnis
inland waterways (ideas)
web frontend
design (current)
- only zoom levels 7 - 15
- only corridor of tiles alongside waterways
- saving bandwidth, tiles not showing a navigable waterway are likely missing (eg. lakes beyond fairways)
workflow (not yet automated)
- query agains my EU-CEMT mbtiles-db to get tilenames
- expand tilenames-set to a corridor
- download tiles
- pack tiles into mbtiles-files
- upload mbtiles-files
bluewater charts (ideas)
web frontend
- die INT-chart Grenzen (als Basis fuer ein download-tool) bekomme ich inzwischen auf den Schirm: http://kap.grade.de/int-chart-ol2.html , sicherlich verbesserungsbeduerftig
- workflow
- INT-Chart durch klick auf die Karte waehlen
- download iniziieren
- Weiterleitung auf neue Seite
- pruefen, ob und in welchem Format vorhanden
- wenn ja und nicht zu alt, downloadlinks anbieten
- wenn nein Job in queue-db schreiben
- number, name, bbox, scale, sessionid, ip-address, email, microtime, status=open, ...
- das ganze garnieren mit
- Pruefung, ob das script fuer diese karte schon in der Warteschlange ist
- Limitierung auf x downloads/jobs pro session ...
- email notifier
- captcha
prerequisites
- apache
- php
- sqlite
tile server
- cronjob checkt queue-db alle x Minuten
- waehlt aeltesten job aus queue-db mit status=open
- tiledownload ueber proxy mit landez
- sinnvolles zoom-level anhand bbox errechnen (max. 10MB/chart)
- mbtiles erzeugen (siehe auch mbtiles implementation)
- stitchen und .png/.jpg erzeugen
- .kap mit imgkap erstellen
- ...
- status=done, zoom-level, date, ... in queue-db setzen
- temp-dir bereinigen und results in charts-dir kopieren
- erste Ergebnisse meiner Versuche: kap.grade.de
prerequisites
py script
- relies on landez
import logging
import shutil
import math
from landez import MBTilesBuilder, ImageExporter, TilesManager
import sqlite3
import os
import sys
import codecs
logging.basicConfig(level=logging.DEBUG)
# to be imported from sqlite-queue
con = sqlite3.connect('queue.sqlite')
with con:
con.row_factory = sqlite3.Row
cur = con.cursor()
cur.execute('SELECT * FROM jobs WHERE status="open" ORDER BY microtime DESC')
rows = cur.fetchall()
if len(rows) == 0:
logging.debug(" Nothing to do, exiting ")
sys.exit(0)
for row in rows:
sqMicrotime = row["Microtime"]
sqChart = row["Chart"]
sqName = row["Name"]
sqHO = row["HO"]
sqBottomGeoPre = row["BottomGeoPre"]
sqTopGeoPre = row["TopGeoPre"]
sqLeftGeoPre = row["LeftGeoPre"]
sqRightGeoPre = row["RightGeoPre"]
sqNumTiles16 = row["NumTiles16"]
# to be calculated from numtiles16, numtiles < 700
# numtiles16 is right now hardcoded at int-chart.gejson for ol depth of view reasons, there might be smarter ways in future
# https://github.com/makinacorpus/landez/issues/44
myNumTiles = sqNumTiles16 * 4 * 4
myZoom = 18
while (myNumTiles > 700):
myNumTiles = myNumTiles / 4
myZoom = myZoom - 1
# settings
# myUrlBase = "http://tiles.grade.de/tiles.py/openriverboatmap/{z}/{x}/{y}.png"
myUrlBase = "http://otile1.mqcdn.com/tiles/1.0.0/map/{z}/{x}/{y}.jpg"
myUrlSeamark = "http://tiles.grade.de/tiles.py/seamark/{z}/{x}/{y}.png"
if sqHO == "DE":
myUrlHydro = "http://tiles.grade.de/tiles.py/NAUTHIS_SkinOfEarth/{z}/{x}/{y}.png"
elif sqHO== "NL/GB":
myUrlHydro = "http://tiles.grade.de/tiles.py/ncp_diepte14/{z}/{x}/{y}.png"
else:
myUrlHydro = ""
myFileName = str(sqMicrotime)
myMbtilesFile = myFileName + ".mbtiles"
myImageFile = myFileName + ".jpg"
myKapFile = myFileName + ".kap"
myTextFile = myFileName + ".txt"
myBbox = (sqLeftGeoPre, sqBottomGeoPre, sqRightGeoPre, sqTopGeoPre)
myZoomLevels = [myZoom]
myZoomLevel = myZoom
myTileSize = 256
#myTileFormat = 'image/png'
myTileFormat = 'image/jpeg'
# download and mbtiles build
mb = MBTilesBuilder(tiles_url=myUrlBase, cache=True, filepath=myMbtilesFile, tile_size=myTileSize, tile_format=myTileFormat)
mb.add_coverage(myBbox, myZoomLevels)
if myUrlHydro != "":
hydro = TilesManager(tiles_url=myUrlHydro)
seamark = TilesManager(tiles_url=myUrlSeamark)
hydro.add_layer(seamark)
overlay = hydro
else:
seamark = TilesManager(tiles_url=myUrlSeamark)
overlay = seamark
mb.add_layer(overlay)
mb.run(force=True)
# export image from mbtiles
# not the best idea: causes IO at temp dir, again
ie = ImageExporter(mbtiles_file=myMbtilesFile)
ie.export_image(myBbox, myZoomLevel, imagepath=myImageFile)
# save the geoinfo
myGrid = ie.grid_tiles(myBbox, myZoomLevel)
sqWidth = len(myGrid[0])
sqHeight = len(myGrid)
sqWidthPix = sqWidth * myTileSize
sqHeightPix = sqHeight * myTileSize
# delete temp dir (ImageExporter does not!)
# bad: path is hard coded. how do I get hold of landez's temp path?
# https://github.com/makinacorpus/landez/issues/42
try:
shutil.rmtree('/tmp/landez')
except OSError:
pass
# geoinfo output
logging.debug(" grid: " + str(myGrid))
myLeftColumn = myGrid[0]
myTopLeft = myLeftColumn[0]
sqLeftTile = myTopLeft[0]
sqTopTile = myTopLeft[-1]
myRightColumn = myGrid[-1]
myBottomRight = myRightColumn[-1]
sqRightTile = myBottomRight[0]
sqBottomTile = myBottomRight[-1]
# calculate coords from tilenumbers
def num2deg(xtile, ytile, zoom):
n = 2.0 ** zoom
lon_deg = -180.0 + 360.0 * xtile / n
lat_rad = math.atan(math.sinh(math.pi * (1.0 - 2.0 * (ytile / n))))
lat_deg = 180.0 * lat_rad / math.pi
return (lon_deg, lat_deg)
sqBottomGeoPost = (num2deg(sqRightTile + 1, sqBottomTile + 1, myZoom)[-1])
sqTopGeoPost = (num2deg(sqLeftTile, sqTopTile, myZoom)[-1])
sqLeftGeoPost = (num2deg(sqLeftTile, sqTopTile, myZoom)[0])
sqRightGeoPost = (num2deg(sqRightTile + 1, sqBottomTile + 1, myZoom)[0])
# shall be exported to sqlite-queue
logging.debug(" baselayer: " + str(myUrlBase))
logging.debug(" seamark: " + str(myUrlSeamark))
logging.debug(" hydro: " + str(myUrlHydro))
logging.debug(" zoom: " + str(myZoom))
logging.debug(" width: " + str(sqWidth) + " tiles")
logging.debug(" height: " + str(sqHeight) + " tiles")
logging.debug(" widthpix: " + str(sqWidthPix) + " pixel")
logging.debug(" heightpix: "+ str(sqHeightPix) + " pixel")
logging.debug(" left tile " + str(sqLeftTile))
logging.debug(" right tile: " + str(sqRightTile))
logging.debug(" top tile: " + str(sqTopTile))
logging.debug(" bottom tile: "+ str(sqBottomTile))
logging.debug(" left: " + str(sqLeftGeoPost))
logging.debug(" right: " + str(sqRightGeoPost))
logging.debug(" top: " + str(sqTopGeoPost))
logging.debug(" bottom: " + str(sqBottomGeoPost))
sqPost = [
'UrlBase="' + str(myUrlBase) + '"',
'UrlSeamark="' + str(myUrlSeamark) + '"',
'UrlHydro="' + str(myUrlHydro) + '"',
'Zoom=' + str(myZoom),
'BottomGeoPost='+ str(sqBottomGeoPost),
'TopGeoPost='+ str(sqTopGeoPost),
'LeftGeoPost='+ str(sqLeftGeoPost),
'RightGeoPost='+ str(sqRightGeoPost),
'status="done"'
]
t = ", "
sqPost = t.join(sqPost)
sqlUpdate = "UPDATE jobs " + "SET " + str(sqPost) + " WHERE Microtime=" + str(sqMicrotime)
logging.debug(" sqlUpdate: " + str(sqlUpdate))
con = sqlite3.connect('queue.sqlite')
with con:
con.row_factory = sqlite3.Row
cur = con.cursor()
cur.execute(sqlUpdate)
con.commit()
logging.debug(" Number of rows updated: " + str(cur.rowcount))
# start imgkap to generate .kap file
import subprocess
myImgkapCall = "./imgkap " + myImageFile + " " + str(sqTopGeoPost) + " " + str(sqLeftGeoPost) + " " + str(sqBottomGeoPost) + " " + str(sqRightGeoPost) + " " + myKapFile
print myImgkapCall
subprocess.call(myImgkapCall, shell=True)
# write textfile
txtPost = [
'These charts cannot replace official charts, use at your own risk!',
'',
'INT Chart Number= ' + sqChart,
'INT Chart Name= ' + sqName,
'',
'Copyright Notices:',
'Base Layer= http://developer.mapquest.com/de/web/products/open/map',
'Seamark Layer= http://www.openseamap.org/',
'DE Hydrography= http://gdiwiki.bsh.de/wiki/index.php/NAUTHIS_Skin_Of_The_Earth',
'NL Hydrography= http://www.rijkswaterstaat.nl/zakelijk/databestanden/online_geografische_gegevens_rijkswaterstaat/',
'',
'Zoom Level= ' + str(myZoom),
'',
'Bottom= '+ str(sqBottomGeoPost),
'Top= '+ str(sqTopGeoPost),
'Left= '+ str(sqLeftGeoPost),
'Right= '+ str(sqRightGeoPost),
'',
'width= ' + str(sqWidth) + ' tiles',
'height= ' + str(sqHeight) + ' tiles',
'width= ' + str(sqWidthPix) + ' pixel',
'height= ' + str(sqHeightPix) + ' pixel',
'',
'left tile number= ' + str(sqLeftTile),
'right tile number= ' + str(sqRightTile),
'top tile number= ' + str(sqTopTile),
'bottom tile number= '+ str(sqBottomTile)
]
t = "\n"
txtPost = t.join(txtPost)
logging.debug(" txtPost: " + txtPost)
with codecs.open(myTextFile, 'w', "utf-8") as f:
f.write(txtPost)
# move and rename files
myPath = "../INT-CHART/INT-"
myNotice = "_MapQuest-OSeaM-BSH"
myNewMbtilesFile = myPath + sqChart + myNotice + ".mbtiles"
myNewImageFile = myPath + sqChart + myNotice + ".jpg"
myNewKapFile = myPath + sqChart + myNotice + ".kap"
myNewTextFile = myPath + sqChart + myNotice + ".txt"
try:
os.rename (myMbtilesFile, myNewMbtilesFile)
os.remove(myMbtilesFile)
except OSError:
pass
try:
os.rename (myImageFile, myNewImageFile)
os.remove(myImageFile)
except OSError:
pass
try:
os.rename (myKapFile, myNewKapFile)
os.remove(myKapFile)
except OSError:
pass
try:
os.rename (myTextFile, myNewTextFile)
os.remove(myTextFile)
except OSError:
pass
php script
<?php
error_reporting(E_ALL);
show_array($_GET);
show_array($_POST);
switch ($_POST['stage'])
{
case 'process':
update();
break;
default :
print_list();
break;
}
function print_form()
{
echo '<form action="'.$_SERVER['PHP_SELF'].'" method="post">';
echo " <input type='submit' name='Text' value='Generate new Chart'/>";
echo " <input type='hidden' name ='stage' value='process' />\n";
echo " <input type='hidden' name ='Chart' value='" . $_GET['Chart'] . "' />\n";
echo " <input type='hidden' name ='Name' value='" . $_GET['Name'] . "' />\n";
echo " <input type='hidden' name ='HO' value='" . $_GET['HO'] . "' />\n";
echo " <input type='hidden' name ='BottomGeoPre' value='" . $_GET['BottomGeoPre'] . "' />\n";
echo " <input type='hidden' name ='TopGeoPre' value='" . $_GET['TopGeoPre'] . "' />\n";
echo " <input type='hidden' name ='LeftGeoPre' value='" . $_GET['LeftGeoPre'] . "' />\n";
echo " <input type='hidden' name ='RightGeoPre' value='" . $_GET['RightGeoPre'] . "' />\n";
echo " <input type='hidden' name ='NumTiles16' value='" . $_GET['NumTiles16'] . "' />\n";
echo "</form>\n";
}
function print_list()
{
if (is_array(glob("../INT-CHART/INT-".$_GET['Chart']."*.*"))) {
foreach (glob("../INT-CHART/INT-".$_GET['Chart']."*.*") as $filename) {
$size = round(filesize($filename)/1024, 2);
$modified = gmdate ( 'd M Y H:i:s', filectime($filename));
echo "<a href='".$filename."'>" .$filename. "</a> - Size: " . $size . " kB - Modified: " . $modified . "</br>";
}
} else {
print_form();
}
}
function update()
{
echo 'Received your request, processing will take some time. Please be patient and check back in an hour ...';
$_POST['Name'] = utf8_encode( $_POST['Name'] );
$db = new SQLite3('../temp/queue.sqlite');
$db->exec("INSERT INTO jobs (
Chart,
Name,
HO,
BottomGeoPre,
TopGeoPre,
LeftGeoPre,
RightGeoPre,
NumTiles16,
status
) VALUES (
'{$_POST['Chart']}',
'{$_POST['Name']}',
'{$_POST['HO']}',
{$_POST['BottomGeoPre']},
{$_POST['TopGeoPre']},
{$_POST['LeftGeoPre']},
{$_POST['RightGeoPre']},
{$_POST['NumTiles16']},
'open'
)");
}
// show content of PHP array, i.e. show_array($_POST)
// this is handy to quickly test what data is in an array
function show_array($x)
{
if (!is_array($x)) return;
reset($x);
echo "<br />";
echo "<b>content of array</b>\n";
if ($x)
while ($i = each($x))
echo "<br />", htmlentities($i[0]), " = ", htmlentities($i[1]), "\n";
echo "<br /><br />\n";
}
?>
Reason for ChartBundler
- Lets talk a bit about amounts here and not low stress or high stress. Using the charts offline on a mobile device. Lets say the display is about 1024x1024 Pixel, then we will need at least 16 tiles per map. If we start on zoom level 8 with the bounding box and continue to zoom level 16 for the harbours, we need around 2 Mio tiles. This 16 tiles on zoom level 8 is about the northern Adria. If we want the Channel just skipping Lands End up to the belgian coast, this would be 6 by 5 on zoom level 8. --Alexej
- Hi Alexej, do not understand this paragraph!? 2 test datasets of mine:
- --Kannix (Diskussion) 16:22, 29. Jan. 2015 (UTC)