Skip to content

Commit fc12bd6

Browse files
Merge pull request #95 from developmentseed/FunctionDirectoryEnv
add TIMVT_FUNCTIONS_DIRECTORY env and switch to maplibre
2 parents 38c1004 + 364e9cb commit fc12bd6

File tree

12 files changed

+145
-39
lines changed

12 files changed

+145
-39
lines changed

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ jobs:
2323
runs-on: ubuntu-latest
2424
strategy:
2525
matrix:
26-
python-version: ['3.7', '3.8', '3.9']
26+
python-version: ['3.7', '3.8', '3.9', '3.10']
2727

2828
steps:
2929
- uses: actions/checkout@v2

CHANGES.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@
99
* add `CacheControlMiddleware` middleware
1010
* enable more options to be forwarded to the `asyncpg` pool creation
1111
* add `PG_SCHEMAS` and `PG_TABLES` environment variable to specify Postgres schemas and tables
12+
* add `TIMVT_FUNCTIONS_DIRECTORY` environment variable to look for function SQL files
13+
* switch viewer to Maplibre
14+
* add `Point` and `LineString` feature support in viewer
15+
* Update dockerfiles to python3.10 and postgres14-postgis3.2
1216

1317
**breaking changes**
1418

dockerfiles/Dockerfile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
ARG PYTHON_VERSION=3.9
1+
ARG PYTHON_VERSION=3.10
22

33
FROM ghcr.io/vincentsarago/uvicorn-gunicorn:${PYTHON_VERSION}
44

dockerfiles/Dockerfile.db

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
1-
FROM ghcr.io/vincentsarago/postgis:13-3.1
1+
FROM ghcr.io/vincentsarago/postgis:14-3.2
22

33
COPY data/*.sql /docker-entrypoint-initdb.d/

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ classifiers = [
1515
"Programming Language :: Python :: 3.7",
1616
"Programming Language :: Python :: 3.8",
1717
"Programming Language :: Python :: 3.9",
18+
"Programming Language :: Python :: 3.10",
1819
"Topic :: Scientific/Engineering :: GIS",
1920
]
2021
dynamic = ["version"]

tests/conftest.py

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ def database_url(test_db):
2323
a database url which we pass to our application through a monkeypatched environment variable.
2424
"""
2525
assert test_db.install_extension("postgis")
26-
test_db.run_sql_file(os.path.join(DATA_DIR, "landsat_wrs.sql"))
26+
test_db.run_sql_file(os.path.join(DATA_DIR, "data", "landsat_wrs.sql"))
2727
assert test_db.has_table("landsat_wrs")
2828
return test_db.connection.engine.url
2929

@@ -34,18 +34,11 @@ def app(database_url, monkeypatch):
3434
monkeypatch.setenv("DATABASE_URL", str(database_url))
3535
monkeypatch.setenv("TIMVT_DEFAULT_MINZOOM", str(5))
3636
monkeypatch.setenv("TIMVT_DEFAULT_MAXZOOM", str(12))
37+
monkeypatch.setenv("TIMVT_FUNCTIONS_DIRECTORY", DATA_DIR)
3738

3839
from timvt.layer import Function
3940
from timvt.main import app
4041

41-
# Register Function to the internal registery
42-
app.state.timvt_function_catalog.register(
43-
Function.from_file(
44-
id="squares",
45-
infile=os.path.join(DATA_DIR, "squares.sql"),
46-
)
47-
)
48-
4942
# Register the same function but we different options
5043
app.state.timvt_function_catalog.register(
5144
Function.from_file(
File renamed without changes.
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
CREATE OR REPLACE FUNCTION landsat_poly_centroid(
2+
-- mandatory parameters
3+
xmin float,
4+
ymin float,
5+
xmax float,
6+
ymax float,
7+
epsg integer,
8+
-- additional parameters
9+
query_params json
10+
)
11+
RETURNS bytea
12+
AS $$
13+
DECLARE
14+
bounds geometry;
15+
tablename text;
16+
result bytea;
17+
BEGIN
18+
WITH
19+
-- Create bbox enveloppe in given EPSG
20+
bounds AS (
21+
SELECT ST_MakeEnvelope(xmin, ymin, xmax, ymax, epsg) AS geom
22+
),
23+
selected_geom AS (
24+
SELECT t.*
25+
FROM public.landsat_wrs t, bounds
26+
WHERE ST_Intersects(t.geom, ST_Transform(bounds.geom, 4326))
27+
),
28+
mvtgeom AS (
29+
SELECT
30+
ST_AsMVTGeom(ST_Transform(ST_Centroid(t.geom), epsg), bounds.geom) AS geom, t.path, t.row
31+
FROM selected_geom t, bounds
32+
UNION
33+
SELECT ST_AsMVTGeom(ST_Transform(t.geom, epsg), bounds.geom) AS geom, t.path, t.row
34+
FROM selected_geom t, bounds
35+
)
36+
SELECT ST_AsMVT(mvtgeom.*, 'default')
37+
38+
-- Put the query result into the result variale.
39+
INTO result FROM mvtgeom;
40+
41+
-- Return the answer
42+
RETURN result;
43+
END;
44+
$$
45+
LANGUAGE 'plpgsql'
46+
IMMUTABLE -- Same inputs always give same outputs
47+
STRICT -- Null input gets null output
48+
PARALLEL SAFE;
49+
50+
COMMENT ON FUNCTION landsat_poly_centroid IS 'Return Combined Polygon/Centroid geometries from landsat table.';

tests/routes/test_metadata.py

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -34,18 +34,28 @@ def test_function_index(app):
3434
response = app.get("/functions.json")
3535
assert response.status_code == 200
3636
body = response.json()
37-
assert len(body) == 2
38-
assert body[0]["id"] == "squares"
39-
assert body[0]["function_name"] == "squares"
40-
assert body[0]["bounds"]
41-
assert body[0]["tileurl"]
42-
assert "options" not in body[0]
37+
assert len(body) == 3
38+
39+
func = list(filter(lambda x: x["id"] == "landsat_poly_centroid", body))[0]
40+
assert func["id"] == "landsat_poly_centroid"
41+
assert func["function_name"] == "landsat_poly_centroid"
42+
assert func["bounds"]
43+
assert func["tileurl"]
44+
assert "options" not in func
45+
46+
func = list(filter(lambda x: x["id"] == "squares", body))[0]
47+
assert func["id"] == "squares"
48+
assert func["function_name"] == "squares"
49+
assert func["bounds"]
50+
assert func["tileurl"]
51+
assert "options" not in func
4352

44-
assert body[1]["id"] == "squares2"
45-
assert body[0]["function_name"] == "squares"
46-
assert body[1]["bounds"] == [0.0, 0.0, 180.0, 90.0]
47-
assert body[1]["tileurl"]
48-
assert body[1]["options"] == [{"name": "depth", "default": 2}]
53+
func = list(filter(lambda x: x["id"] == "squares2", body))[0]
54+
assert func["id"] == "squares2"
55+
assert func["function_name"] == "squares"
56+
assert func["bounds"] == [0.0, 0.0, 180.0, 90.0]
57+
assert func["tileurl"]
58+
assert func["options"] == [{"name": "depth", "default": 2}]
4959

5060

5161
def test_function_info(app):

timvt/main.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
1-
"""TiVTiler app."""
1+
"""TiMVT application."""
2+
3+
import pathlib
24

35
from timvt import __version__ as timvt_version
46
from timvt.db import close_db_connection, connect_to_db, register_table_catalog
57
from timvt.errors import DEFAULT_STATUS_CODES, add_exception_handlers
68
from timvt.factory import TMSFactory, VectorTilerFactory
7-
from timvt.layer import FunctionRegistry
9+
from timvt.layer import Function, FunctionRegistry
810
from timvt.middleware import CacheControlMiddleware
911
from timvt.settings import ApiSettings, PostgresSettings
1012

@@ -50,6 +52,15 @@
5052

5153
# We add the function registry to the application state
5254
app.state.timvt_function_catalog = FunctionRegistry()
55+
if settings.functions_directory:
56+
functions = pathlib.Path(settings.functions_directory).glob("*.sql")
57+
for func in functions:
58+
name = func.name
59+
if name.endswith(".sql"):
60+
name = name[:-4]
61+
app.state.timvt_function_catalog.register(
62+
Function.from_file(id=name, infile=str(func))
63+
)
5364

5465

5566
# Register Start/Stop application event handler to setup/stop the database connection

0 commit comments

Comments
 (0)