OpenSeaMap-dev Diskussion:Downloadable Raster Charts: Unterschied zwischen den Versionen

Aus OpenSeaMap-dev
Wechseln zu: Navigation, Suche
(Die Seite wurde neu angelegt: „== Reason == * Hi Alexej, do not understand this paragraph!? 2 test datasets of mine: ** INT-1463 bei zoom-level 13 (s="53.7" n="54.25" w="7.4" e="9.05"):<br>x…“)
 
(inland waterways added)
 
(14 dazwischenliegende Versionen desselben Benutzers werden nicht angezeigt)
Zeile 1: Zeile 1:
== Reason ==
+
== 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 ===
 +
* 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 [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
 +
* temp-dir bereinigen und results in charts-dir kopieren
 +
* erste Ergebnisse meiner Versuche: [http://kap.grade.de/INT-CHART/ kap.grade.de]
 +
 
 +
==== prerequisites ====
 +
* apache
 +
** mod_python
 +
* tilestache
 +
* nginx remote proxy
 +
* imagemagick
 +
* python
 +
** [https://github.com/makinacorpus/landez landez]
 +
* [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 ==
 +
* 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. --[[Benutzer:Humbach-OSM|Alexej]]
 +
 
 
* Hi Alexej, do not understand this paragraph!? 2 test datasets of mine:
 
* Hi Alexej, do not understand this paragraph!? 2 test datasets of mine:
 
** INT-1463 bei zoom-level 13 (s="53.7" n="54.25" w="7.4" e="9.05"):<br>x: 37<br>y: 21<br>total: 777<br>[http://kap.grade.de/Europe/Germany/]
 
** INT-1463 bei zoom-level 13 (s="53.7" n="54.25" w="7.4" e="9.05"):<br>x: 37<br>y: 21<br>total: 777<br>[http://kap.grade.de/Europe/Germany/]
 
** river Ruhr<br>zoomlevel=16<br>~ 370 tiles<br>[http://wiki.openstreetmap.org/wiki/KAP-Dateien_aus_OSeaMap_erstellen#Python-Tool]
 
** river Ruhr<br>zoomlevel=16<br>~ 370 tiles<br>[http://wiki.openstreetmap.org/wiki/KAP-Dateien_aus_OSeaMap_erstellen#Python-Tool]
 
: --[[Benutzer:Kannix|Kannix]] ([[Benutzer Diskussion:Kannix|Diskussion]]) 16:22, 29. Jan. 2015 (UTC)
 
: --[[Benutzer:Kannix|Kannix]] ([[Benutzer Diskussion:Kannix|Diskussion]]) 16:22, 29. Jan. 2015 (UTC)

Aktuelle Version vom 22. Juni 2015, 13:01 Uhr

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

  • apache
    • mod_python
  • tilestache
  • nginx remote proxy
  • imagemagick
  • python
  • imgkap

py script


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:
    • INT-1463 bei zoom-level 13 (s="53.7" n="54.25" w="7.4" e="9.05"):
      x: 37
      y: 21
      total: 777
      [1]
    • river Ruhr
      zoomlevel=16
      ~ 370 tiles
      [2]
--Kannix (Diskussion) 16:22, 29. Jan. 2015 (UTC)