I'm David Lacroix, Geo data engineer @MeilleursAgents
WMTS + IGN js api @Geoportail
Geojson + Mapboxgl.js @Etalab
Vector tile + Mapboxgl.js @Koumoul
Source Gaffuri (2012)
Source Mapzen
OGC - Vector Tiles Pilot
Postgres![]() |
↦ | Flask![]() |
↦ | MapboxGL![]() |
find more information in that great tutorial
read more from Paul Ramsey
your web application is your tile server
mapbox vector tile specification
$ sudo docker-compose up -d
http://localhost:8001/map_empty
map.on('load', function () {
map.addLayer({
"id": "haussmann",
"type": "fill",
"source": {
"type": "geojson",
// path to geojson (text, file, url...)
"data": "static/resource/haussmann.geojson"
},
"paint": {
"fill-color": "#088",
"fill-opacity": 0.8
}
});
});
http://localhost:8001/my-layer/{z}/{x}/{y}
# z: zoom level (0-22)
# x: tilegrid X coordinate
# y: tilegrid Y coordinate
@app.route("/map_geojson")
def map_geojson():
return render_template('map_geojson.html', token=MAPBOX_TOKEN)
@app.route('/<string:layer>/<int:z>/<int:x>/<int:y>', methods=['GET'])
def generic_mvt(layer, z, x, y):
srid = int(request.args.get('srid', 4326))
tile = _load_tile(layer, x, y, z, srid=srid)
response = make_response(tile)
response.headers.add('Content-Type', 'application/octet-stream')
response.headers.add('Access-Control-Allow-Origin', '*')
return response
def _load_tile(layer_name, x, y, z, srid=4326):
tile = None
def _load_tile(layer_name, x, y, z, srid=4326):
tile = None
xmin, ymin, xmax, ymax = _tms2bbox(x, y, z)
query = '''
...
'''.format(
schema=TILE_SCHEMA,
table_name=layer_name,
)
query_parameters = {
'layer_name': layer_name,
'xmin': xmin, 'ymin': ymin, 'xmax': xmax, 'ymax': ymax,
'srid_bbox': srid
}
with psycopg2.connect(**DB_PARAMETERS) as connection:
with connection.cursor() as cursor:
cursor.execute(query, query_parameters)
res = cursor.fetchone()
if 'mvt' in res and res['mvt'] is not None:
tile = bytes(res['mvt'])
return tile
read more from Paul Ramsey
SELECT ST_AsMVT(tile, %(layer_name)s, 4096, 'mvt_geom') AS mvt
FROM (
SELECT id,
value,
extrude,
ST_AsMVTGeom(
-- Geometry field
ST_Transform(t.geom, 3857),
-- MVT tile boundary
ST_Makebox2d(
-- Lower left coordinate
ST_Transform(ST_SetSrid(ST_MakePoint(%(xmin)s, %(ymin)s), 4326), 3857),
-- Upper right coordinate
ST_Transform(ST_SetSrid(ST_MakePoint(%(xmax)s, %(ymax)s), 4326), 3857)
),
4096 /*Extent*/, 256 /*Buffer*/, TRUE /*Clip geom*/) AS mvt_geom
FROM {schema}.{table_name} t
WHERE
ST_Makebox2d(
ST_Transform(ST_SetSrid(ST_MakePoint(%(xmin)s, %(ymin)s), 4326), %(srid_bbox)s),
ST_Transform(ST_SetSrid(ST_MakePoint(%(xmax)s, %(ymax)s), 4326), %(srid_bbox)s)
) && t.geom
) AS tile
$ psql -h localhost -p 5555 -d postgres -U postgres -c "\d+ mvt.*"
View "mvt.apur_building"
Column | Type | Collation | Nullable | Default | Storage | Description
---------+-----------------------------+-----------+----------+---------+---------+-------------
id | integer | | | | plain |
value | integer | | | | plain |
extrude | integer | | | | plain |
geom | geometry(MultiPolygon,4326) | | | | main |
View definition:
SELECT apur_building_raw.ogc_fid AS id,
(regexp_match(apur_building_raw.c_perconst, '[0-9]{4}'))[1]::integer AS value,
apur_building_raw.h_med::integer AS extrude,
apur_building_raw.wkb_geometry AS geom
FROM apur_building_raw;
http://localhost:8001/map_apur
map.addLayer({
"id": "apur_building",
"type": "fill",
"source": {
"type": "vector",
"tiles": ["http://localhost:8001/apur_building/{z}/{x}/{y}"],
"minzoom": 11,
"maxzoom": 21
},
"source-layer": "apur_building",
"paint": {
"fill-color": "#ffab40",
"fill-opacity": 0.7
},
});
http://localhost:8001/map_apur?show_color=true
{"paint": {
"fill-color": [
"interpolate",
["linear"],
["number", ["get", "value"], 0],
0,
"#aaa",
1800,
"#dd2c00",
2010,
"#ffd600",
],
"fill-opacity": .7
}}
// Separating data source to render the same data with different style
data_source = {
"type": "vector",
"tiles": ["http://localhost:8001/apur_building/{z}/{x}/{y}?srid=4326"],
"minzoom": 12,
"maxzoom": 21
}
map.addSource('apur_building', data_source);
apur_building_line = {
"id": "apur_building_line",
"type": "line",
"source": 'apur_building',
"paint": {
map.addLayer({
"id": "apur_building_label",
"type": "symbol",
"source": "apur_building",
"source-layer": "apur_building",
"minzoom": 18,
"layout": {
"text-field": ["get", "value"],
"text-size": 14,
"text-font": ["Open Sans Semibold", "Arial Unicode MS Bold"],
},
});
map.on("mousemove", 'apur_building', function(e){
map.getCanvas().style.cursor = 'pointer';
if (e.features.length > 0) {
idDisplay.textContent = e.features[0].properties.id;
valueDisplay.textContent = e.features[0].properties.value;
extrusionDisplay.textContent = e.features[0].properties.extrude;
hoveredStateId = e.features[0].id;
// set the hover attribute to true
map.setFeatureState(
{source: 'apur_building', id: hoveredStateId, 'sourceLayer': 'apur_building'},
{hover: true}
);
}
});
{"paint": {
"fill-outline-color": "#000099",
"fill-color": "#000099",
"fill-opacity": [
"case", ["boolean", ["feature-state", "hover"], false],
1,
0
]
}}
Error: The feature id parameter must be provided and non-negative.

if ('id' in this.properties) {
this.id = this.properties.id;
}
$ npm install -g @mapbox/vt2geojson
$ vt2geojson http://localhost:8001/apur_building/{z}/{x}/{y}
read more mapnik vs postgis
$ sudo docker build -t data-master data-processing/
$ sudo docker run \
--link=postgres-master \
--network=global-network \
--env-file .env \
-it data-master
https://github.com/DavidLacroix/postgis-mvt