Search code examples
pythonmapsgeospatial

Interactive map in a closed network


I have geo coordinates data that i need to display on a map in a closed network.

Most libraries related to maps require an internet connection folium or map box.

As such i have no idea where or how to begin.


Solution

  • (requires a Linux system)

    Credit to this guide.

    Download the area of the map you wish to display from geofabrik set up a localized tile server using a docker run the following to set up the docker :

    docker volume create openstreetmap-data
    # For the fastest possible import time, increase the number of threads to
    # match what is available on your CPU.
    # You should also increase the amount of memory available to OSM2PGSQL to 
    # about 75% of your RAM.
    docker run -v <full path to downloaded file>:/data/region.osm.pbf \
               -v openstreetmap-data:/data/database/ \
               -e THREADS=4 \
               -e "OSM2PGSQL_EXTRA_ARGS=-C 4096" \
               overv/openstreetmap-tile-server:2.2.0 import
    

    To initialize the tile server:

        # Adjust the threads and memory as required
    docker run -p 8080:80 \
               -v openstreetmap-data:/data/database/ -d \
               -e THREADS=4 \
               -e "OSM2PGSQL_EXTRA_ARGS=-C 4096" \
               --shm-size=256m \
               overv/openstreetmap-tile-server:2.2.0 run
    

    Go to http://localhost:8080 to check the server

    Pre-rendering the map is recommended

    docker exec -it  <docker_container_name> /bin/bash
    
    # Run this Inside the docker container 
    wget https://raw.githubusercontent.com/alx77/render_list_geo.pl/master/render_list_geo.pl
    chmod +x render_list_geo.pl
    #./render_list_geo.pl -x <left> -X <right> -y <bottom> -Y <top> \
    #                     -z 0 -Z <max_zoom> -n <number_of_threads> -m ajt
    # For example to render the Isle of Wight from zoom level 7 to 15 using 2 threads
    ./render_list_geo.pl -x -1.6 -X -1.05 -y 50.57 -Y 50.79 -z 7 -Z 15 -n 2 -m ajt
    # Once it completes you can disconnect from the container
    exit
    

    To download the tile run the following python code using the this expression as an example

    python3 tile_download.py -x -1.6 -X -1.05 -y 50.57 -Y 50.79 -Z 15

    #!/usr/bin/python3
    # tile_download.py
    # Adapted from https://gist.github.com/tonyrewin/9444410 (tonyrewin)
    import os
    import math
    import urllib.request
    import os.path
    import argparse
    
    def deg2num(lat_deg, lon_deg, zoom):
        lat_rad = math.radians(lat_deg)
        n = 2.0 ** zoom
        xtile = int((lon_deg + 180.0) / 360.0 * n)
        ytile = int((1.0 - math.log(math.tan(lat_rad) + \
                    (1 / math.cos(lat_rad))) / math.pi) / 2.0 * n)
        return (xtile, ytile)
    
    def download_url(zoom, xtile, ytile):
    
        url = "http://localhost:8080/tile/%d/%d/%d.png" % (zoom, xtile, ytile)
        dir_path = "tiles/%d/%d/" % (zoom, xtile)
        download_path = "tiles/%d/%d/%d.png" % (zoom, xtile, ytile)
    
        if not os.path.exists(dir_path):
            os.makedirs(dir_path)
    
        if(not os.path.isfile(download_path)):
            urllib.request.urlretrieve(url, download_path)
    
    if __name__ == "__main__":
    
        parser = argparse.ArgumentParser()
        parser.add_argument("-x", type=float, help="Minimum longitude")
        parser.add_argument("-X", type=float, help="Maximum longitude")
        parser.add_argument("-y", type=float, help="Minimum latitude")
        parser.add_argument("-Y", type=float, help="Minimum latitude")
        parser.add_argument("-Z", type=int, help="Maximum zoom level (>=7)")
        args = parser.parse_args()
    
        # Zoom 0 to 6 download worldwide tiles
        for zoom in range(0,7):
            for x in range(0,2**zoom):
                for y in range(0,2**zoom):
                    download_url(zoom, x, y)
    
        for zoom in range(7, int(args.Z)+1):
            xtile_min, ytile_min = deg2num(float(args.y), float(args.x), zoom)
            xtile_max, ytile_max = deg2num(float(args.Y), float(args.X), zoom)
    
            print(f"Z:{zoom}, X:{xtile_min}-{xtile_max}, Y:{ytile_max}-{ytile_min}")
            for x in range(xtile_min, xtile_max + 1):
                for y in range(ytile_min, ytile_max - 1, -1):                
                    result = download_url(zoom, x, y)
    

    To Clean up run:

    # Identify the name of the openstreetmap-tile-server container
    docker ps -a
    # Stop the container and delete all data (except the downloaded PNG tiles!)
    docker stop <name_of_container>
    docker rm <name_of_container>
    docker volume rm openstreetmap-data
    docker image rm overv/openstreetmap-tile-server:2.2.0
    

    Than all that is left is to build a Leaflet web app

    good luck