Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions compose/martin.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
services:
varnish:
image: varnish:7.5
container_name: varnish
container_name: tiler_varnish
ports:
- "6081:6081"
volumes:
Expand All @@ -18,7 +18,7 @@ services:
- martin

martin:
container_name: martin
container_name: tiler_server_martin
image: rub21/tiler-server-martin:v1
build:
context: ../images/tiler-server-martin
Expand Down
4 changes: 2 additions & 2 deletions hetzner/deploy.sh
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,9 @@ if [[ "$ACTION" == "start" || "$ACTION" == "restart" ]] && [ "$AUTO_YES" != "tru
fi

case "$ACTION" in
start) $COMPOSE up -d ;;
start) $COMPOSE up -d --build;;
stop) $COMPOSE down ;;
restart) $COMPOSE up -d --force-recreate ;;
restart) $COMPOSE up -d --force-recreate --build ;;
*) echo "Unknown action: $ACTION"; exit 1 ;;
esac

Expand Down
6 changes: 2 additions & 4 deletions hetzner/tiler/tiler.production.yml
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ services:

tiler_server_martin:
container_name: tiler_server_martin
image: ghcr.io/openhistoricalmap/tiler-server-martin:0.0.1-0.dev.git.3349.h2739b4b1
image: ghcr.io/openhistoricalmap/tiler-server-martin:0.0.1-0.dev.git.3359.h756000d2
restart: always
environment:
- OHM_DOMAIN=${OHM_DOMAIN:-openhistoricalmap.org}
Expand All @@ -88,9 +88,7 @@ services:
ports:
- "3030:80"
networks:
ohm_network:
aliases:
- martin
- ohm_network

tiler_varnish:
container_name: tiler_varnish
Expand Down
59 changes: 52 additions & 7 deletions hetzner/tiler/tiler.staging.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,55 @@
services:

volumes:
tiler_pgdata:
driver: local
name: tiler_db_11_02
tiler_server_martin:
container_name: tiler_server_martin
image: ohm/tiler-server-martin:v1
build:
context: ../../images/tiler-server-martin
dockerfile: Dockerfile
restart: always
environment:
- OHM_DOMAIN=${OHM_DOMAIN:-openhistoricalmap.org}
env_file:
- .env.tiler
ports:
- "3030:80"
networks:
- ohm_network

tiler_imposm_data:
driver: local
name: tiler_imposm_11_02
tiler_varnish:
container_name: tiler_varnish
image: varnish:7.5
restart: always
ports:
- "6081:6081"
volumes:
- ../../images/tiler-varnish/default.vcl:/etc/varnish/default.vcl:ro
tmpfs:
- /var/lib/varnish/varnishd:exec
command: >
varnishd -F
-a :6081
-f /etc/varnish/default.vcl
-s dynamic=malloc,${VARNISH_DYNAMIC_SIZE:-16G}
-s static=malloc,${VARNISH_STATIC_SIZE:-4G}
-p thread_pool_min=100
-p thread_pool_max=2000
-p thread_pools=2
-p workspace_backend=256k
-p workspace_client=256k
-p http_resp_hdr_len=32k
-p http_resp_size=256k
-p nuke_limit=1000
-p timeout_idle=30
-p feature=+http2
mem_limit: 4G
cpus: "2.0"
ulimits:
memlock: -1
nofile:
soft: 131072
hard: 131072
networks:
- ohm_network
depends_on:
- tiler_server_martin
23 changes: 21 additions & 2 deletions images/tiler-server-martin/scripts/generate_functions.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,22 @@ def load_config():
}


# Per-group attribution strings (see openhistoricalmap/issues/1343).
# OHM groups link to the copyright page without "©" or "contributors", since
# OHM does not claim copyright over all contributed data.
OHM_ATTRIBUTION = '<a href="https://www.openhistoricalmap.org/copyright">OpenHistoricalMap</a>'
OSM_ATTRIBUTION = '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
NE_ATTRIBUTION = '<a href="https://www.naturalearthdata.com/about/terms-of-use/">Natural Earth</a>'

GROUP_ATTRIBUTION = {
"ohm": OHM_ATTRIBUTION,
"ohm_admin": OHM_ATTRIBUTION,
"ohm_other_boundaries": OHM_ATTRIBUTION,
"osm_land": OSM_ATTRIBUTION,
"ne": NE_ATTRIBUTION,
}


def get_columns(cur, table_name, exclude):
"""Get column names from a table/mview, excluding specified columns."""
exclude = list(exclude) + ALWAYS_EXCLUDE
Expand Down Expand Up @@ -171,14 +187,14 @@ def get_maxzoom(zoom_mapping):
return 20 if last_max is None else last_max


def build_tilejson(name, description, tiles_url, vector_layers, minzoom=0, maxzoom=20):
def build_tilejson(name, description, tiles_url, vector_layers, attribution, minzoom=0, maxzoom=20):
"""Build a TileJSON 3.0.0 manifest."""
return {
"tilejson": "3.0.0",
"name": name,
"description": description,
"version": "1.0.0",
"attribution": "&copy; <a href=\"https://www.openhistoricalmap.org/copyright\">OpenHistoricalMap</a> contributors",
"attribution": attribution,
"scheme": "xyz",
"tiles": [tiles_url],
"minzoom": minzoom,
Expand Down Expand Up @@ -207,6 +223,7 @@ def generate_tilejson_files(groups, fields_per_function, base_url):

for group in groups:
group_name = group["name"]
attribution = GROUP_ATTRIBUTION.get(group_name, OHM_ATTRIBUTION)
group_vector_layers = []
group_minzoom = 20
group_maxzoom = 0
Expand All @@ -231,6 +248,7 @@ def generate_tilejson_files(groups, fields_per_function, base_url):
description=f"Layer: {func_def['source_layer']}",
tiles_url=f"{base_url}/maps/{group_name}/{fn}/{{z}}/{{x}}/{{y}}",
vector_layers=[vl],
attribution=attribution,
minzoom=fn_minzoom,
maxzoom=fn_maxzoom,
)
Expand All @@ -246,6 +264,7 @@ def generate_tilejson_files(groups, fields_per_function, base_url):
description=f"Composite: {group_name} ({len(group_vector_layers)} layers)",
tiles_url=f"{base_url}/maps/{group_name}/{{z}}/{{x}}/{{y}}",
vector_layers=group_vector_layers,
attribution=attribution,
minzoom=group_minzoom,
maxzoom=group_maxzoom,
)
Expand Down
32 changes: 20 additions & 12 deletions images/tiler-server-martin/static/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -352,7 +352,13 @@ <h2>Martin</h2>
};
sourceLayerNames[fnName] = vl.id;
});
groupsData.push({ name: groupName, functions: fns });
groupsData.push({
name: groupName,
functions: fns,
attribution: tj.attribution,
minzoom: tj.minzoom,
maxzoom: tj.maxzoom,
});
});

showGroupList();
Expand Down Expand Up @@ -464,23 +470,20 @@ <h2>Martin</h2>
}

async function addCompositeSource(group) {
const compositeFns = group.functions.join(',');
const sourceId = `src-composite-${group.name}`;
// Use /maps/{group}/ route which goes through nginx cache + fresh_tiles support,
// instead of Martin's native TileJSON URLs which bypass cache.
const cachedTileUrl = `${MARTIN_BASE}/maps/${group.name}/{z}/{x}/{y}.pbf`;
const tileJsonUrl = `${MARTIN_BASE}/${compositeFns}`;

try {
const resp = await fetch(tileJsonUrl);
const tj = await resp.json();

map.addSource(sourceId, {
const compositeSourceOpts = {
type: 'vector',
tiles: [tileUrlForMode(cachedTileUrl)],
minzoom: tj.minzoom || 0,
maxzoom: tj.maxzoom || 22,
});
minzoom: group.minzoom != null ? group.minzoom : 0,
maxzoom: group.maxzoom != null ? group.maxzoom : 22,
};
if (group.attribution) compositeSourceOpts.attribution = group.attribution;
map.addSource(sourceId, compositeSourceOpts);

const layerIds = [];
const layerByFn = {};
Expand Down Expand Up @@ -519,7 +522,7 @@ <h2>Martin</h2>
});

activeSources['__composite__'] = { sourceId, layerIds, layerByFn, color: '#4fc3f7' };
if (freshTilesMode || cacheBuster > 0) applyTileUrls();
if (freshTilesMode) applyTileUrls();
} catch (e) {
console.error('Failed to add composite source:', e);
}
Expand Down Expand Up @@ -612,6 +615,11 @@ <h2>Martin</h2>
minzoom: tj.minzoom || 0,
maxzoom: tj.maxzoom || 22,
};
// Prefer group attribution from /maps/{group}.json — Martin's per-function
// TileJSON does not include the custom per-group attribution.
const group = activeGroupIdx != null ? groupsData[activeGroupIdx] : null;
const attribution = (group && group.attribution) || tj.attribution;
if (attribution) sourceOpts.attribution = attribution;
if (tj.scheme) sourceOpts.scheme = tj.scheme;
if (tj.bounds) sourceOpts.bounds = tj.bounds;
map.addSource(sourceId, sourceOpts);
Expand Down Expand Up @@ -665,7 +673,7 @@ <h2>Martin</h2>
}

activeSources[name] = { sourceId, layerIds, color };
if (freshTilesMode || cacheBuster > 0) applyTileUrls();
if (freshTilesMode) applyTileUrls();
const el = document.getElementById(`src-item-${name}`);
if (el) el.classList.add('active');
}
Expand Down
14 changes: 10 additions & 4 deletions images/tiler-varnish/default.vcl
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
vcl 4.1;

backend martin {
.host = "martin";
.host = "tiler_server_martin";
.port = "80";
.connect_timeout = 10s;
.first_byte_timeout = 120s;
Expand Down Expand Up @@ -51,15 +51,21 @@ sub vcl_backend_response {
unset beresp.http.Set-Cookie;

if (bereq.url ~ "^/maps/(ne|osm_land)/") {
# Static tiles: storage separado, TTL casi infinito
# Static tiles: separate storage, near-infinite TTL
set beresp.storage = storage.static;
set beresp.ttl = 365d;
set beresp.grace = 30d;
set beresp.keep = 7d;
} else if (bereq.url ~ "^/maps/[^/]+/[0-5]/") {
# Dynamic low zooms (0-5): not invalidated via BAN, refreshed by short TTL
set beresp.storage = storage.dynamic;
set beresp.ttl = 24h;
set beresp.grace = 1h;
set beresp.keep = 1d;
} else {
# Dynamic tiles: TTL largo como safety net; BAN invalida antes
# Dynamic mid/high zooms (6-20): long TTL; BAN invalidates earlier
set beresp.storage = storage.dynamic;
set beresp.ttl = 7d;
set beresp.ttl = 5d;
set beresp.grace = 1h;
set beresp.keep = 1d;
}
Expand Down
Loading