diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json index f4e3299..bc6defd 100644 --- a/.devcontainer/devcontainer.json +++ b/.devcontainer/devcontainer.json @@ -1,6 +1,6 @@ { "dockerComposeFile": [ - "../dc-simple.yml" + "../docker-compose.yml" ], "service": "devenv" } diff --git a/README.md b/README.md index e470779..223f080 100644 --- a/README.md +++ b/README.md @@ -27,27 +27,79 @@ Go to the [workshop repository](https://github.com/maplibre/workshop), in the to After downloading the Docker image that we prepared for you you will be dropped into a shell. If not, click the small Plus sign above the terminal window to create another shell. +### Quick Start + +The workshop now includes a complete Docker Compose setup that provides all the services you need: + +1. **Generate tiles** (run once): + ```bash + docker compose --profile setup up planetiler + ``` + +2. **Start the workshop environment**: + ```bash + docker compose up -d + ``` + +3. **Import OSM data to PostgreSQL** (optional): + ```bash + docker compose --profile import up osm2pgsql + ``` + +Once started, you can access: +- πŸ“– **Main workshop page**: http://localhost:8080 +- 🎨 **Maputnik editor**: http://localhost:8080/maputnik/ +- πŸ—ΊοΈ **Martin tile server**: http://localhost:8080/tiles/ + ## 1. Tile Generation We already downloaded a Boston OSM extract created with [slice.openstreetmap.us](https://slice.openstreetmap.us/). -Run the following command. +### Option A: Use Docker Compose (recommended) + +```bash +docker compose --profile setup up planetiler +``` + +This will generate both the base map tiles and bench overlay tiles automatically. + +### Option B: Manual tile generation + +Run the following command to generate base map tiles: +```bash +java -jar /planetiler.jar --download_dir=/data/sources --minzoom=0 --maxzoom=14 --osm_path=/data/sources/boston.osm.pbf --output=/data/output.mbtiles ``` -java -jar /planetiler.jar --download_dir=/data/sources --minzoom=0 --maxzoom=14 --osm_path=/data/sources/boston.osm.pbf + +To generate bench overlay tiles: + +```bash +java -jar /planetiler.jar /scripts/benches.yaml --download_dir=/data/sources --minzoom=0 --maxzoom=14 --osm_path=/data/sources/boston.osm.pbf --output=/data/benches.mbtiles ``` #### Expected Result -You should see planetiler generate a new file `data/output.mbtiles`. Use `ll data/output.mbtiles` to see it. +You should see planetiler generate new files: +- `data/output.mbtiles` (base map) +- `data/benches.mbtiles` (bench overlay) + +Use `ls -la data/*.mbtiles` to see them. ## 2. Tile Serving -The MBTiles file that was generated in the previous step can be hosted with a tile server. In this workshop we will use Martin, which is pre-installed to the development container. +The MBTiles files that were generated in the previous step can be hosted with a tile server. In this workshop we use Martin, which is included in our Docker Compose setup. -Run the following command: +### Option A: Use Docker Compose (recommended) +```bash +docker compose up -d ``` -martin data/output.mbtiles + +This starts all services including Martin tile server, accessible at http://localhost:8080/tiles/ + +### Option B: Run Martin manually + +```bash +martin data/output.mbtiles data/benches.mbtiles ``` Martin will launch on port 3000. You will get a prompt to expose this port. Expose the port to the internet. @@ -76,9 +128,17 @@ Go to the catalog, and then to `/output`. This is a TileJSON endpoint which desc ## 3. Styling your map +### Using the integrated setup + +With the complete workshop environment running, you can access Maputnik directly at http://localhost:8080/maputnik/ + +The Martin tile server is available at http://localhost:8080/tiles/ for use in Maputnik. + +### Manual setup + Go to [Maputnik](https://maplibre.org/maputnik), click open and open the empty style. Next, add a new source. Use the URL of your Martin instance with the `/output` TileJSON endpoint. -Since you don't have any layers, you will not see anything visualized. Hower, you can switch from the 'Map' view to the 'Inspect' view to see the data contained in your tiles. If it looks something like this, you are doing great so far! +Since you don't have any layers, you will not see anything visualized. However, you can switch from the 'Map' view to the 'Inspect' view to see the data contained in your tiles. If it looks something like this, you are doing great so far! ![alt text](image-1.png) @@ -104,16 +164,59 @@ Take a closer look to the generated HMTL to understand how MapLibre GL JS is set ## 5. Creating a custom overlay with Planetiler -[`scripts/benches.yaml`](./scripts/benches.yaml) describes how to create [custom map tiles](https://github.com/onthegomap/planetiler/blob/main/planetiler-custommap/README.md) with planetiler containing all of the benches in Boston. Run it using the following command: +[`scripts/benches.yaml`](./scripts/benches.yaml) describes how to create [custom map tiles](https://github.com/onthegomap/planetiler/blob/main/planetiler-custommap/README.md) with planetiler containing all of the benches in Boston. -``` +If you used the automated setup, this was already done for you. Otherwise, run it using the following command: + +```bash java -jar /planetiler.jar scripts/benches.yaml --download_dir=/data/sources --minzoom=0 --maxzoom=14 --osm_path=/data/sources/boston.osm.pbf --output=data/benches.mbtiles ``` Then run martin again to serve those tiles: -``` +```bash martin data/output.mbtiles data/benches.mbtiles ``` And you can add the source to Maputnik using `https://{public URL for your Martin instance}/benches` + +## 6. Working with PostgreSQL and PostGIS + +The workshop includes a PostgreSQL database with PostGIS extension for storing and serving vector data. + +### Import OSM data to PostgreSQL + +Run the following command to import bicycle parking data: + +```bash +docker compose --profile import up osm2pgsql +``` + +This imports bicycle parking locations from the Boston OSM extract into PostgreSQL using osm2pgsql. + +### Verify the import + +Check the imported data: + +```bash +docker compose exec db psql -U postgres -d estonia -c "SELECT COUNT(*) FROM bicycle_parking;" +``` + +### Access the database + +The PostgreSQL database is available at `localhost:5432` with: +- Database: `estonia` +- User: `postgres` +- Password: `password` + +Martin is configured to serve this data as vector tiles at the `/bicycle_parking` endpoint. + +### Manual OSM import + +The import command above runs automatically with the correct profile: + +```bash +docker compose --profile import up osm2pgsql +``` + +The import script ([`scripts/bicycle_parking.lua`](./scripts/bicycle_parking.lua)) defines how OSM bicycle parking amenities are processed and stored in PostgreSQL. diff --git a/dc-simple.yml b/dc-simple.yml deleted file mode 100644 index d583458..0000000 --- a/dc-simple.yml +++ /dev/null @@ -1,22 +0,0 @@ -services: - frontend: - image: nginx:alpine - restart: unless-stopped - ports: - - "8080:8080" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - ./html:/usr/share/nginx/html - depends_on: - - maputnik - - maputnik: - image: ghcr.io/maplibre/maputnik:main - restart: unless-stopped - expose: - - "80" - - devenv: - image: ghcr.io/maplibre/workshop:boston - restart: unless-stopped - command: sleep infinity diff --git a/dc-with-db.yml b/dc-with-db.yml deleted file mode 100644 index bdb2a86..0000000 --- a/dc-with-db.yml +++ /dev/null @@ -1,78 +0,0 @@ -services: - frontend: - image: nginx:alpine - restart: unless-stopped - ports: - # These two ports must be identical, and must match the one in nginx.conf - - "8080:8080" - volumes: - - ./nginx.conf:/etc/nginx/nginx.conf - - ./html:/usr/share/nginx/html - depends_on: - - martin - - maputnik - networks: - - dbnet - - maputnik: - image: ghcr.io/maplibre/maputnik:main - restart: unless-stopped - expose: - - "80" - networks: - - dbnet - - martin: - image: ghcr.io/maplibre/martin:latest - restart: unless-stopped - expose: - - "3000" - volumes: - - ./data:/data - command: - - /data/estonia.mbtiles - - postgresql://postgres:password@db/estonia -# - /data/benches.mbtiles - depends_on: - - db - networks: - - dbnet - - db: - image: postgis/postgis:16-3.4-alpine - restart: unless-stopped - environment: - - POSTGRES_DB=estonia - - POSTGRES_USER=postgres - - POSTGRES_PASSWORD=password - networks: - - dbnet -# volumes: -# # persist PostgreSQL data in a local directory outside the docker container -# - ./pg_data:/var/lib/postgresql/data - - osm2pgsql: - profiles: ["manual"] - image: iboates/osm2pgsql:latest - restart: 'no' - command: - - -d - - postgresql://postgres:password@db/estonia - - -O - - flex - - -S - - /scripts/bicycle_parking.lua - - /data/estonia-latest.osm.pbf - environment: - - DATABASE_URL=postgresql://postgres:password@db - volumes: - - ./data:/data - - ./scripts:/scripts - depends_on: - - db - networks: - - dbnet - -networks: - dbnet: - external: true diff --git a/docker-compose.yml b/docker-compose.yml new file mode 100644 index 0000000..258eb91 --- /dev/null +++ b/docker-compose.yml @@ -0,0 +1,97 @@ +services: + # Frontend proxy to serve the web interface and route requests + frontend: + image: nginx:alpine + restart: unless-stopped + ports: + # These two ports must be identical, and must match the one in nginx.conf + - "8080:8080" + volumes: + - ./nginx.conf:/etc/nginx/nginx.conf + - ./html:/usr/share/nginx/html + depends_on: + - martin + - maputnik + + # Maputnik map style editor + maputnik: + image: ghcr.io/maplibre/maputnik:main + restart: unless-stopped + expose: + - "80" + + # Martin tile server + martin: + image: ghcr.io/maplibre/martin:latest + restart: unless-stopped + expose: + - "3000" + volumes: + - ./data:/data + command: + - /data/output.mbtiles + - /data/benches.mbtiles + - postgresql://postgres:password@db/estonia + depends_on: + - db + + # Development environment with planetiler and martin CLI tools + devenv: + image: ghcr.io/maplibre/workshop:boston + restart: unless-stopped + command: sleep infinity + volumes: + - ./data:/data + - ./scripts:/scripts + + # Planetiler service to generate MBTiles from OSM data + planetiler: + image: ghcr.io/maplibre/workshop:boston + restart: 'no' + profiles: ["setup"] + volumes: + - ./data:/output + - ./scripts:/scripts + working_dir: / + command: > + sh -c " + echo 'Generating base map tiles...' && + java -jar /planetiler.jar --download_dir=/data/sources --minzoom=0 --maxzoom=14 --osm_path=/data/sources/boston.osm.pbf --output=/output/output.mbtiles && + echo 'Generating bench overlay tiles...' && + java -jar /planetiler.jar /scripts/benches.yaml --download_dir=/data/sources --minzoom=0 --maxzoom=14 --osm_path=/data/sources/boston.osm.pbf --output=/output/benches.mbtiles && + echo 'Tile generation complete!' + " + + # PostgreSQL database with PostGIS extension + db: + image: postgis/postgis:16-3.4-alpine + restart: unless-stopped + environment: + - POSTGRES_DB=estonia + - POSTGRES_USER=postgres + - POSTGRES_PASSWORD=password + ports: + - "5432:5432" + # Uncomment to persist data across container restarts + # volumes: + # - ./pg_data:/var/lib/postgresql/data + + # osm2pgsql service to import OSM data into PostgreSQL + osm2pgsql: + profiles: ["import"] + image: ghcr.io/maplibre/workshop:boston + restart: 'no' + command: > + sh -c " + echo 'Installing osm2pgsql...' && + apt-get update && apt-get install -y osm2pgsql && + echo 'Importing OSM data...' && + osm2pgsql -d postgresql://postgres:password@db/estonia -O flex -S /scripts/bicycle_parking.lua /data/sources/boston.osm.pbf && + echo 'OSM data import complete!' + " + environment: + - DATABASE_URL=postgresql://postgres:password@db/estonia + volumes: + - ./scripts:/scripts + depends_on: + - db diff --git a/nginx.conf b/nginx.conf index 9b5bd68..6e4cab3 100644 --- a/nginx.conf +++ b/nginx.conf @@ -4,32 +4,31 @@ http { access_log /var/log/nginx/access.log; error_log /var/log/nginx/error.log; - upstream martin_backend { - server martin:3000; - } - upstream maputnik_backend { - server maputnik:80; - } + # Enable runtime DNS resolution + resolver 127.0.0.11 valid=30s; server { # This port must match the one in docker-compose.yml listen 8080; location ~ /tiles/(?.*) { + set $martin_backend "martin:3000"; proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Host $host:$server_port; proxy_set_header X-Rewrite-URL $uri; proxy_redirect off; - proxy_pass http://martin_backend/$fwd_path$is_args$args; + proxy_pass http://$martin_backend/$fwd_path$is_args$args; } location ~ /maputnik/(?.*) { - proxy_pass http://maputnik_backend/$fwd_path$is_args$args; + set $maputnik_backend "maputnik:80"; + proxy_pass http://$maputnik_backend/$fwd_path$is_args$args; } location ~ /assets { - proxy_pass http://maputnik_backend/$request_uri; + set $maputnik_backend "maputnik:80"; + proxy_pass http://$maputnik_backend$request_uri; } location ~ / {