diff --git a/CHANGES.md b/CHANGES.md index e8e57e3..2751428 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -1,3 +1,7 @@ +# 0.14.0 (2025-03-20) + +* add `--geojson` option to add a GeoJSON Feature or FeatureCollection on the map viewer + # 0.13.1 (2025-03-19) * relaxe titiler requirement to `>=0.20,<0.22` diff --git a/rio_viz/app.py b/rio_viz/app.py index 25bc158..aa957fe 100644 --- a/rio_viz/app.py +++ b/rio_viz/app.py @@ -93,6 +93,8 @@ class viz: layers: Optional[List[str]] = attr.ib(default=None) nodata: Optional[Union[str, int, float]] = attr.ib(default=None) + geojson: Optional[Dict] = attr.ib(default=None) + # cog / bands / assets reader_type: str = attr.ib(init=False) @@ -803,6 +805,7 @@ def viewer(request: Request): "info_endpoint": str(request.url_for("info_geojson")), "point_endpoint": str(request.url_for("point")), "allow_3d": has_mvt, + "geojson": self.geojson, }, media_type="text/html", ) diff --git a/rio_viz/scripts/cli.py b/rio_viz/scripts/cli.py index 65cd980..a4b42da 100644 --- a/rio_viz/scripts/cli.py +++ b/rio_viz/scripts/cli.py @@ -1,6 +1,7 @@ """rio_viz.cli.""" import importlib +import json import os import tempfile import warnings @@ -135,6 +136,11 @@ def convert(self, value, param, ctx): callback=options_to_dict, help="Reader Options.", ) +@click.option( + "--geojson", + type=click.File(mode="r"), + help="GeoJSON Feature or FeatureCollection path to display on viewer.", +) def viz( src_path, nodata, @@ -148,6 +154,7 @@ def viz( server_only, config, reader_params, + geojson, ): """Rasterio Viz cli.""" if reader: @@ -187,6 +194,7 @@ def viz( maxzoom=maxzoom, nodata=nodata, layers=layers, + geojson=json.load(geojson) if geojson else None, ) if not server_only: click.echo(f"Viewer started at {application.template_url}", err=True) diff --git a/rio_viz/templates/assets.html b/rio_viz/templates/assets.html index c75744b..9873632 100644 --- a/rio_viz/templates/assets.html +++ b/rio_viz/templates/assets.html @@ -365,6 +365,21 @@ document.getElementById('zoom').textContent = z }) +const add_geojson = () => { + if (map.getSource('geojson')) map.removeSource('geojson') + if (map.getLayer('geojson')) map.removeLayer('geojson') + if ({{ geojson }} !== undefined) { + map.addSource('geojson', {'type': 'geojson', 'data': {{ geojson|safe }}}) + map.addLayer({ + id: 'geojson', + type: 'line', + source: 'geojson', + layout: {'line-cap': 'round', 'line-join': 'round'}, + paint: {'line-color': '#FF0000', 'line-width': 4} + }) + } +} + const add_raster = (tilejson) => { if (map.getLayer('raster-l')) map.removeLayer('raster-l') if (map.getSource('raster-l')) map.removeSource('raster-l') @@ -442,6 +457,9 @@ document.getElementById('loader').classList.add('off') if (scope.metadata) addHisto1Band() }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -485,6 +503,9 @@ document.getElementById('loader').classList.add('off') if (scope.metadata) addHisto3Bands() }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -712,6 +733,10 @@ if (map.getLayer('raster')) map.removeLayer('raster') if (map.getSource('raster')) map.removeSource('raster') + // remove GeoJSON layers/sources + if (map.getSource('geojson')) map.removeSource('geojson') + if (map.getLayer('geojson')) map.removeLayer('geojson') + const rasterType = document.getElementById('toolbar').querySelector(".active").id switch (rasterType) { case '1b': diff --git a/rio_viz/templates/bands.html b/rio_viz/templates/bands.html index 058fc19..94a6185 100644 --- a/rio_viz/templates/bands.html +++ b/rio_viz/templates/bands.html @@ -380,6 +380,21 @@ document.getElementById('zoom').textContent = z }) +const add_geojson = () => { + if (map.getSource('geojson')) map.removeSource('geojson') + if (map.getLayer('geojson')) map.removeLayer('geojson') + if ({{ geojson }} !== undefined) { + map.addSource('geojson', {'type': 'geojson', 'data': {{ geojson|safe }}}) + map.addLayer({ + id: 'geojson', + type: 'line', + source: 'geojson', + layout: {'line-cap': 'round', 'line-join': 'round'}, + paint: {'line-color': '#FF0000', 'line-width': 4} + }) + } +} + const add_raster = (tilejson) => { if (map.getLayer('raster-l')) map.removeLayer('raster-l') if (map.getSource('raster-l')) map.removeSource('raster-l') @@ -586,6 +601,9 @@ .then(data => { add_raster(data) }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -601,6 +619,9 @@ add_vector_source(data) add_vector_layer(vizType) }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -616,6 +637,9 @@ add_vector_source(data) add_vector_layer(vizType) }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -654,6 +678,9 @@ add_raster(data) if (scope.dataset_statistics) addHisto3Bands() }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -676,6 +703,10 @@ if (map.getLayer('vector')) map.removeLayer('vector') if (map.getSource('vector')) map.removeSource('vector') + // remove GeoJSON layers/sources + if (map.getSource('geojson')) map.removeSource('geojson') + if (map.getLayer('geojson')) map.removeLayer('geojson') + const rasterType = document.getElementById('toolbar').querySelector(".active").id switch (rasterType) { case '1b': @@ -948,6 +979,7 @@ switchViz() } else { add_vector_layer(newViz) + add_geojson() if (scope.dataset_statistics) addHisto1Band() } } diff --git a/rio_viz/templates/index.html b/rio_viz/templates/index.html index f179bdf..8de9e3f 100644 --- a/rio_viz/templates/index.html +++ b/rio_viz/templates/index.html @@ -381,6 +381,21 @@ document.getElementById('zoom').textContent = z }) +const add_geojson = () => { + if (map.getSource('geojson')) map.removeSource('geojson') + if (map.getLayer('geojson')) map.removeLayer('geojson') + if ({{ geojson }} !== undefined) { + map.addSource('geojson', {'type': 'geojson', 'data': {{ geojson|safe }}}) + map.addLayer({ + id: 'geojson', + type: 'line', + source: 'geojson', + layout: {'line-cap': 'round', 'line-join': 'round'}, + paint: {'line-color': '#FF0000', 'line-width': 4} + }) + } +} + const add_raster = (tilejson) => { if (map.getLayer('raster-l')) map.removeLayer('raster-l') if (map.getSource('raster-l')) map.removeSource('raster-l') @@ -583,6 +598,9 @@ .then(data => { add_raster(data) }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -598,6 +616,9 @@ add_vector_source(data) add_vector_layer(vizType) }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -613,6 +634,9 @@ add_vector_source(data) add_vector_layer(vizType) }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -651,6 +675,9 @@ add_raster(data) if (scope.dataset_statistics) addHisto3Bands() }) + .then(() => { + add_geojson() + }) .catch(err => { console.warn(err) }) @@ -673,6 +700,10 @@ if (map.getLayer('vector')) map.removeLayer('vector') if (map.getSource('vector')) map.removeSource('vector') + // remove GeoJSON layers/sources + if (map.getSource('geojson')) map.removeSource('geojson') + if (map.getLayer('geojson')) map.removeLayer('geojson') + const rasterType = document.getElementById('toolbar').querySelector(".active").id switch (rasterType) { case '1b': @@ -945,6 +976,7 @@ switchViz() } else { add_vector_layer(newViz) + add_geojson() if (scope.dataset_statistics) addHisto1Band() } }