Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Tile server migration #262

Open
wants to merge 16 commits into
base: main
Choose a base branch
from
Open
688 changes: 345 additions & 343 deletions application/factory.py

Large diffs are not rendered by default.

99 changes: 99 additions & 0 deletions application/routers/tiles.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
from fastapi import APIRouter, HTTPException, Depends, Response
from sqlalchemy.orm import Session
import math
from sqlalchemy import text
from application.db.session import get_session

router = APIRouter()


def tile_is_valid(z, x, y, fmt):
max_tile = 2**z - 1
return 0 <= x <= max_tile and 0 <= y <= max_tile and fmt in ["pbf", "mvt"]


def tile_bounds(z, x, y):
n = 2.0**z
lon_min = x / n * 360.0 - 180.0
lat_min = math.degrees(math.atan(math.sinh(math.pi * (1 - 2 * y / n))))
lon_max = (x + 1) / n * 360.0 - 180.0
lat_max = math.degrees(math.atan(math.sinh(math.pi * (1 - 2 * (y + 1) / n))))
return lon_min, lat_min, lon_max, lat_max


def build_db_query(tile, session: Session):
z, x, y, dataset = tile["zoom"], tile["x"], tile["y"], tile["dataset"]
lon_min, lat_min, lon_max, lat_max = tile_bounds(z, x, y)

geometry_column = "geometry"
if dataset == "tree":
geometry_column = "point"

tile_width = 256

mvt_geom_query = text(
f"""SELECT ST_AsMVT(q, :dataset, :tile_width, 'geom') FROM
(SELECT
ST_AsMVTGeom(
{geometry_column},
ST_MakeEnvelope(:lon_min, :lat_min, :lon_max, :lat_max, 4326),
:tile_width,
4096,
true
) as geom,
jsonb_build_object(
'name', entity.name,
'dataset', entity.dataset,
'organisation-entity', entity.organisation_entity,
'entity', entity.entity,
'entry-date', entity.entry_date,
'start-date', entity.start_date,
'end-date', entity.end_date,
'prefix', entity.prefix,
'reference', entity.reference
) AS properties
FROM entity
WHERE NOT EXISTS (
SELECT 1 FROM old_entity
WHERE entity.entity = old_entity.old_entity
)
AND dataset = :dataset
AND ST_Intersects({geometry_column}, ST_MakeEnvelope(:lon_min, :lat_min, :lon_max, :lat_max, 4326))
) AS q
"""
)

result = session.execute(
mvt_geom_query,
{
"lon_min": lon_min,
"lat_min": lat_min,
"lon_max": lon_max,
"lat_max": lat_max,
"dataset": dataset,
"tile_width": tile_width,
},
).scalar()
return result


@router.get("/{dataset}/{z}/{x}/{y}.vector.{fmt}")
async def read_tiles(
dataset: str,
z: int,
x: int,
y: int,
fmt: str,
session: Session = Depends(get_session),
):
if not tile_is_valid(z, x, y, fmt):
raise HTTPException(status_code=400, detail="Invalid tile path")

tile = {"dataset": dataset, "zoom": z, "x": x, "y": y, "format": fmt}
mvt_data = build_db_query(tile, session)
if not mvt_data:
raise HTTPException(status_code=404, detail="Tile data not found")

return Response(
content=mvt_data.tobytes(), media_type="application/vnd.mapbox-vector-tile"
)
2 changes: 1 addition & 1 deletion application/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@ class Settings(BaseSettings):
RELEASE_TAG: Optional[str] = None
ENVIRONMENT: str
DATASETTE_URL: HttpUrl
DATASETTE_TILES_URL: Optional[HttpUrl]
DATA_FILE_URL: HttpUrl
GA_MEASUREMENT_ID: Optional[str] = None
OS_CLIENT_KEY: Optional[str] = None
Expand All @@ -28,6 +27,7 @@ def get_settings() -> Settings:
# TODO remove as Gov PaaS is no longer needed
# Gov.uk PaaS provides a URL to the postgres instance it provisions via DATABASE_URL
# See https://docs.cloud.service.gov.uk/deploying_services/postgresql/#connect-to-a-postgresql-service-from-your-app

if "DATABASE_URL" in os.environ:
database_url = os.environ["DATABASE_URL"].replace(
"postgres://", "postgresql://", 1
Expand Down
6 changes: 3 additions & 3 deletions application/templates/components/map/macro.jinja
Original file line number Diff line number Diff line change
Expand Up @@ -53,14 +53,14 @@
params = {
...params,
baseTileStyleFilePath: "/static/javascripts/base-tile.json",
vectorSource: "{{ params.DATASETTE_TILES_URL }}/-/tiles/dataset_tiles/{z}/{x}/{y}.vector.pbf",
datasetVectorUrl: "{{ params.DATASETTE_TILES_URL }}/-/tiles/",
vectorSource: "https://www.development.digital-land.info/tiles/{z}/{x}/{y}.vector.pbf",
datasetVectorUrl: "https://www.development.digital-land.info/tiles/",
datasets: {{layers|tojson}}.map(d => d.dataset),
vectorTileSources: {{layers|tojson}}.map(d => {
d.paint_options = d.paint_options || {};
return {
name: d.dataset,
vectorSource: "{{ params.DATASETTE_TILES_URL }}/-/tiles/" + d.dataset + "/{z}/{x}/{y}.vector.pbf",
vectorSource: "https://www.development.digital-land.info/tiles/" + d.dataset + "/{z}/{x}/{y}.vector.pbf",
dataType: d.paint_options.type,
styleProps: {
colour: d.paint_options.colour,
Expand Down
87 changes: 34 additions & 53 deletions application/templates/national-map.html
Original file line number Diff line number Diff line change
@@ -1,58 +1,39 @@
{% extends "layouts/layout--full-width.html" %}
{% set templateName = "dl-info/national-map.html" %}

{%- from "components/map/macro.jinja" import map %}

{% set containerClasses = 'dl-container--full-width' %}
{% set fullWidthHeader = true %}

{% set includesMap = true %}
{% block pageTitle %}Map of planning data for England | Planning Data{% endblock %}

{%
set notePanel = '<p>Find, understand and download the <a href="/dataset" class="govuk-link">datasets used to create this map</a>.</p>'
%}

{%- block mapAssets %}
<script src='/static/javascripts/maplibre-gl.js'></script>
<link href='/static/stylesheets/maplibre-gl.css' rel='stylesheet' />
{{ super() }}
{% endblock -%}

{%- from "components/back-button/macro.jinja" import dlBackButton %}
{% block breadcrumbs%}
{{ dlBackButton({
"parentHref": '/'
})}}
{% endblock %}

{% block content %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<h1 class="govuk-heading-xl govuk-!-margin-bottom-4">Map of planning data for England</h1>
</div>
{% extends "layouts/layout--full-width.html" %} {% set templateName =
"dl-info/national-map.html" %} {%- from "components/map/macro.jinja" import map
%} {% set containerClasses = 'dl-container--full-width' %} {% set
fullWidthHeader = true %} {% set includesMap = true %} {% block pageTitle %}Map
of planning data for England | Planning Data{% endblock %} {% set notePanel = '
<p>
Find, understand and download the
<a href="/dataset" class="govuk-link">datasets used to create this map</a>.
</p>
' %} {%- block mapAssets %}
<script src="/static/javascripts/maplibre-gl.js"></script>
<link href="/static/stylesheets/maplibre-gl.css" rel="stylesheet" />
{{ super() }} {% endblock -%} {%- from "components/back-button/macro.jinja"
import dlBackButton %} {% block breadcrumbs%} {{ dlBackButton({ "parentHref":
'/' })}} {% endblock %} {% block content %}
<div class="govuk-grid-row">
<div class="govuk-grid-column-full">
<h1 class="govuk-heading-xl govuk-!-margin-bottom-4">
Map of planning data for England
</h1>
</div>
</div>

<p id="aria-label-national-map" class="govuk-body-l">See the data we've collected and collated on a map.</p>

{{
map({
'height': 700,
'layers': layers,
'DATASETTE_TILES_URL': settings.DATASETTE_TILES_URL,
'notePanel': notePanel,
'enableZoomControls': true,
'enableLayerControls': true,
'enableZoomCounter': true,
})
}}

<p class="govuk-body govuk-!-margin-bottom-0 govuk-!-width-two-thirds">This prototype map is automatically created using data from planning.data.gov.uk. Find out more <a href="/about">about the Planning Data Platform</a></p>

{% endblock %}
<p id="aria-label-national-map" class="govuk-body-l">
See the data we've collected and collated on a map.
</p>

{% block bodyEnd %}
{{ super() }}
{{ map({ 'height': 700, 'layers': layers, 'DATASETTE_TILES_URL':
"https://www.development.digital-land.info", 'notePanel': notePanel,
'enableZoomControls': true, 'enableLayerControls': true, 'enableZoomCounter':
true, }) }}

<p class="govuk-body govuk-!-margin-bottom-0 govuk-!-width-two-thirds">
This prototype map is automatically created using data from
planning.data.gov.uk. Find out more
<a href="/about">about the Planning Data Platform</a>
</p>

{% endblock %}
{% endblock %} {% block bodyEnd %} {{ super() }} {% endblock %}
Loading
Loading