Skip to content

Commit 3d35161

Browse files
authored
Merge pull request #225 from UW-Macrostrat/tileserver-updates
Tileserver updates
2 parents 88f43c8 + fd8b5c8 commit 3d35161

File tree

8 files changed

+129
-43
lines changed

8 files changed

+129
-43
lines changed

local-root/configs/tileserver-cache.vcl

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,10 @@ sub vcl_backend_response {
6363
set beresp.ttl = 1d;
6464
# Allow stale content while revalidating
6565
set beresp.grace = 5m;
66+
} else if (beresp.http.Content-Type ~ "application/x-protobuf") {
67+
# Vector tiles should have the same TTL
68+
set beresp.ttl = 1d;
69+
set beresp.grace = 5m;
6670
} else {
6771
# Shorter TTL for other content
6872
set beresp.ttl = 5m;

local-root/docker-compose.yaml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -83,6 +83,8 @@ services:
8383
environment:
8484
- VARNISH_HTTP_PORT=8000
8585
volumes:
86+
# TODO:
87+
# Changes to this config file aren't pulled in without explicitly restarting the container.
8688
- ./configs/tileserver-cache.vcl:/etc/varnish/default.vcl
8789
tileserver_core:
8890
image: hub.opensciencegrid.org/macrostrat/tileserver:main
@@ -112,7 +114,7 @@ services:
112114
py-modules: ../py-modules
113115
environment:
114116
- DATABASE_URL=postgresql://${POSTGRES_USER}:${POSTGRES_PASSWORD}@database:5432/${POSTGRES_DB}
115-
- MAPNIK_POOL_SIZE=64
117+
- MAPNIK_POOL_SIZE=8
116118
command: >
117119
uvicorn --host 0.0.0.0 --port 8000
118120
macrostrat.legacy_tileserver:app

services/legacy-tileserver/macrostrat/legacy_tileserver/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ async def startup_event():
3939
# Tile rendering map pool size
4040
# This controls how many image tiles can be concurrently rendered.
4141
# Database access is somewhat inefficient, so we may need to adjust this.
42-
mapnik_pool_size = int(environ.get("MAPNIK_POOL_SIZE", "64"))
42+
mapnik_pool_size = int(environ.get("MAPNIK_POOL_SIZE", "8"))
4343
log.info(f"Setting up Mapnik map pool with size {mapnik_pool_size}")
4444

4545
app.state.pool = await create_pool_b(url)

services/legacy-tileserver/macrostrat/legacy_tileserver/image_tiles/__init__.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,12 +31,17 @@ async def get_image_tile(request: Request, args: CachedTileArgs) -> bytes:
3131
scale = scale_for_zoom(tile.z)
3232
box = Box2d(bbox.left, bbox.top, bbox.right, bbox.bottom)
3333

34+
# TODO: tune PostGIS data sources
35+
# https://github.com/mapnik/mapnik/wiki/PostGIS
36+
3437
async with pool.map_context(scale) as _map:
3538

3639
_map.zoom_to_box(box)
3740

3841
# Render map to image
3942
im = Image(512, 512)
4043
render(_map, im, 2)
44+
4145
# Return image as binary
42-
return im.tostring("png")
46+
res = im.tostring("png")
47+
return res

services/legacy-tileserver/macrostrat/legacy_tileserver/image_tiles/mapnik_styles.py

Lines changed: 83 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -8,56 +8,60 @@
88
from subprocess import CalledProcessError, check_output
99
from textwrap import dedent
1010

11+
from mapnik import Datasource
12+
1113
from .config import layer_order
1214

1315
__here__ = Path(__file__).parent
1416

1517

16-
def make_carto_stylesheet(scale, db_url):
18+
def make_datasource(db_url, **kwargs):
1719
pg_credentials = get_credentials(db_url)
20+
return Datasource(
21+
**pg_credentials,
22+
**kwargs,
23+
)
1824

19-
line_sql = " UNION ALL ".join(
20-
f"SELECT * FROM lines.{s}" for s in layer_order[scale]
25+
26+
def make_line_datasource(db_url, scale):
27+
line_query = create_line_query(scale)
28+
pg_credentials = get_credentials(db_url)
29+
return Datasource(
30+
type="postgis",
31+
table=f"({line_query}) subset",
32+
key_field="line_id",
33+
geometry_field="geom",
34+
extent_cache="auto",
35+
extent="-180,-90,180,90",
36+
srid="4326",
37+
**pg_credentials,
38+
)
39+
40+
41+
def make_polygon_datasource(db_url, scale):
42+
polygon_query = create_polygon_query(scale)
43+
pg_credentials = get_credentials(db_url)
44+
return Datasource(
45+
type="postgis",
46+
table=f"({polygon_query}) subset",
47+
key_field="map_id",
48+
geometry_field="geom",
49+
extent_cache="auto",
50+
extent="-180,-90,180,90",
51+
srid="4326",
52+
**pg_credentials,
2153
)
2254

55+
56+
def make_carto_stylesheet(scale, db_url):
57+
pg_credentials = get_credentials(db_url)
58+
2359
cartoCSS = (__here__ / "style.mss").read_text()
2460

2561
webmercator_srs = "+proj=merc +a=6378137 +b=6378137 +lat_ts=0.0 +lon_0=0.0 +x_0=0.0 +y_0=0.0 +k=1.0 +units=m +nadgrids=@null +wktext +no_defs +over"
2662

27-
polygon_query = dedent(
28-
f"""
29-
SELECT
30-
z.map_id,
31-
nullif(l.color, '') AS color,
32-
z.geom FROM carto.polygons z
33-
LEFT JOIN maps.map_legend
34-
ON z.map_id = map_legend.map_id
35-
LEFT JOIN maps.legend AS l
36-
ON l.legend_id = map_legend.legend_id
37-
LEFT JOIN maps.sources
38-
ON l.source_id = sources.source_id
39-
WHERE sources.status_code = 'active'
40-
AND l.color IS NOT NULL
41-
AND l.color != ''
42-
AND z.scale = '{scale}'
43-
"""
44-
)
45-
46-
line_query = dedent(
47-
f"""
48-
SELECT
49-
x.line_id,
50-
x.geom,
51-
q.direction,
52-
q.type
53-
FROM carto.lines x
54-
LEFT JOIN ( {line_sql} ) q
55-
ON q.line_id = x.line_id
56-
LEFT JOIN maps.sources ON x.source_id = sources.source_id
57-
WHERE sources.status_code = 'active'
58-
AND x.scale = '{scale}'
59-
"""
60-
)
63+
polygon_query = create_polygon_query(scale)
64+
line_query = create_line_query(scale)
6165

6266
return {
6367
"bounds": [-89, -179, 89, 179],
@@ -153,3 +157,45 @@ def get_credentials(db_url=None):
153157
"password": db_url.password,
154158
"dbname": db_url.database,
155159
}
160+
161+
162+
def create_polygon_query(scale):
163+
return dedent(
164+
f"""
165+
SELECT
166+
z.map_id,
167+
nullif(l.color, '') AS color,
168+
z.geom FROM carto.polygons z
169+
LEFT JOIN maps.map_legend
170+
ON z.map_id = map_legend.map_id
171+
LEFT JOIN maps.legend AS l
172+
ON l.legend_id = map_legend.legend_id
173+
LEFT JOIN maps.sources
174+
ON l.source_id = sources.source_id
175+
WHERE sources.status_code = 'active'
176+
AND l.color IS NOT NULL
177+
AND l.color != ''
178+
AND z.scale = '{scale}'
179+
"""
180+
)
181+
182+
183+
def create_line_query(scale):
184+
line_sql = " UNION ALL ".join(
185+
f"SELECT * FROM lines.{s}" for s in layer_order[scale]
186+
)
187+
return dedent(
188+
f"""
189+
SELECT
190+
x.line_id,
191+
x.geom,
192+
q.direction,
193+
q.type
194+
FROM carto.lines x
195+
LEFT JOIN ( {line_sql} ) q
196+
ON q.line_id = x.line_id
197+
LEFT JOIN maps.sources ON x.source_id = sources.source_id
198+
WHERE sources.status_code = 'active'
199+
AND x.scale = '{scale}'
200+
"""
201+
)

services/legacy-tileserver/macrostrat/legacy_tileserver/image_tiles/pool.py

Lines changed: 30 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,11 @@
88
from macrostrat.utils import get_logger
99

1010
from .config import scales
11-
from .mapnik_styles import make_mapnik_xml
11+
from .mapnik_styles import (
12+
make_line_datasource,
13+
make_mapnik_xml,
14+
make_polygon_datasource,
15+
)
1216

1317
log = get_logger(__name__)
1418

@@ -19,6 +23,9 @@ class MapnikMapPool:
1923
storage: dict[str, Queue[Map, None]] = {}
2024
n_instances: int = 4
2125

26+
line_datasources: dict[str, object] = {}
27+
polygon_datasources: dict[str, object] = {}
28+
2229
def __init__(self, n_instances: int = 4):
2330
self.n_instances = n_instances
2431

@@ -32,19 +39,41 @@ async def setup_queue(self, db: Database, scale: str):
3239
# Fill the queue with Mapnik maps
3340
t = time.time()
3441
_xml = make_mapnik_xml(scale, db.engine.url)
42+
43+
# Set up PostGIS data sources for shared use here
44+
45+
line_datasource = make_line_datasource(db.engine.url, scale)
46+
polygon_datasource = make_polygon_datasource(db.engine.url, scale)
47+
48+
self.line_datasources[scale] = line_datasource
49+
self.polygon_datasources[scale] = polygon_datasource
50+
3551
log.info(f"Generated mapnik XML for scale {scale} in {time.time() - t} seconds")
3652
for _ in range(self.n_instances):
3753
_map = Map(512, 512)
3854
load_map_from_string(_map, _xml)
55+
# Set up shared data sources here
56+
for layer in _map.layers:
57+
if layer.name == f"lines_{scale}":
58+
layer.datasource = line_datasource
59+
elif layer.name == f"units_{scale}":
60+
layer.datasource = polygon_datasource
3961
await q.put(_map)
62+
dt = time.time() - t
63+
log.info(
64+
f"Initialized {self.n_instances} map objects for scale {scale} in {dt} seconds"
65+
)
4066
return q
4167

4268
@asynccontextmanager
4369
async def map_context(self, scale: str) -> Map:
4470
"""Get a map from the pool."""
4571
q = self.storage[scale]
4672
_map = await q.get()
73+
t = time.time()
4774
try:
4875
yield _map
4976
finally:
5077
await q.put(_map)
78+
dt = time.time() - t
79+
log.debug(f"Returned map to pool for scale {scale} in {dt:.3f} seconds")

services/legacy-tileserver/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "macrostrat.legacy-tileserver"
3-
version = "2.3.0-beta.3"
3+
version = "2.3.0"
44
description = "Macrostrat legacy tile server"
55
authors = [{ name = "Daven Quinn", email = "[email protected]" }]
66
requires-python = ">=3.9,<3.10"

services/legacy-tileserver/uv.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)