diff --git a/examples/3d_terrain.jGIS b/examples/3d_terrain.jGIS index 7a2fc024..7622b82c 100644 --- a/examples/3d_terrain.jGIS +++ b/examples/3d_terrain.jGIS @@ -1,18 +1,9 @@ { "layerTree": [ "a82ef521-e727-4209-a5a0-145d66f18a06", - "733327dc-c367-420c-9ee1-0510ee521457" + "f12a8dfe-4674-43e7-a649-cd49ee83eb34" ], "layers": { - "733327dc-c367-420c-9ee1-0510ee521457": { - "name": "Custom Hillshade Layer Layer", - "parameters": { - "shadowColor": "#473B24", - "source": "1f457f60-5d31-45f6-8066-4585aabd5629" - }, - "type": "HillshadeLayer", - "visible": true - }, "a82ef521-e727-4209-a5a0-145d66f18a06": { "name": "OpenStreetMap.Mapnik Layer", "parameters": { @@ -20,26 +11,25 @@ }, "type": "RasterLayer", "visible": true + }, + "f12a8dfe-4674-43e7-a649-cd49ee83eb34": { + "name": "Custom Hillshade Layer", + "parameters": { + "shadowColor": "#473B24", + "source": "e87bb91a-ff2a-485b-bf0e-14b3b848955a" + }, + "type": "HillshadeLayer", + "visible": true } }, "options": { "bearing": 0.0, - "latitude": 47.27340570466396, - "longitude": 11.540279388427734, + "latitude": 47.25967099028631, + "longitude": 11.418052209432549, "pitch": 59.00000000000003, "zoom": 11.829283007646955 }, "sources": { - "1f457f60-5d31-45f6-8066-4585aabd5629": { - "name": "Custom Raster DEM Source", - "parameters": { - "attribution": "w", - "tileSize": 256.0, - "url": "https://demotiles.maplibre.org/terrain-tiles/tiles.json", - "urlParameters": {} - }, - "type": "RasterDemSource" - }, "ceef4036-b757-44bf-8a21-42c6c99dab72": { "name": "OpenStreetMap.Mapnik", "parameters": { @@ -52,11 +42,13 @@ }, "type": "RasterSource" }, - "cffe76e7-fa97-445a-98dc-a2861f5782ca": { - "name": "Terrain tile source", + "e87bb91a-ff2a-485b-bf0e-14b3b848955a": { + "name": "Custom Hillshade Layer Source", "parameters": { - "tileSize": 256.0, - "url": "https://demotiles.maplibre.org/terrain-tiles/tiles.json" + "encoding": "mapbox", + "tileSize": 512.0, + "url": "https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png", + "urlParameters": {} }, "type": "RasterDemSource" } diff --git a/examples/buildings.jGIS b/examples/buildings.jGIS index 8997fed6..0801cc7b 100644 --- a/examples/buildings.jGIS +++ b/examples/buildings.jGIS @@ -13,7 +13,7 @@ "sourceLayer": "bingmlbuildings", "type": "fill" }, - "type": "VectorLayer", + "type": "VectorTileLayer", "visible": true }, "f99eb7b0-5e38-4078-b310-36a0746472aa": { @@ -26,11 +26,12 @@ } }, "options": { - "latitude": 41.86704023051254, - "longitude": -87.64128426232207, - "zoom": 8.089610803710057, "bearing": 0.0, - "pitch": 0.0 + "latitude": 41.86704023051257, + "longitude": -87.64128426232207, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 8.089610803710057 }, "sources": { "7a7ee6fd-c1e2-4c5d-a4e2-a7974db138a4": { @@ -55,5 +56,6 @@ }, "type": "RasterSource" } - } -} + }, + "terrain": {} +} \ No newline at end of file diff --git a/examples/earthquakes-no-filter.jGIS b/examples/earthquakes-no-filter.jGIS new file mode 100644 index 00000000..b7b72121 --- /dev/null +++ b/examples/earthquakes-no-filter.jGIS @@ -0,0 +1,64 @@ +{ + "layerTree": [ + "f907e26c-c4c8-4c2d-ad62-813f63ed9de9", + "0336896f-f7ce-460f-8d11-07a589ff03d8" + ], + "layers": { + "0336896f-f7ce-460f-8d11-07a589ff03d8": { + "filters": { + "appliedFilters": [], + "logicalOp": "all" + }, + "name": "Custom GeoJSON Layer", + "parameters": { + "color": "#FF0000", + "opacity": 1.0, + "source": "d07cc573-51fb-4ae8-965b-a0082ace7f2b", + "type": "circle" + }, + "type": "VectorLayer", + "visible": true + }, + "f907e26c-c4c8-4c2d-ad62-813f63ed9de9": { + "name": "OpenStreetMap.Mapnik Layer", + "parameters": { + "source": "5fe556bc-9938-4217-8de6-fe25aef088c2" + }, + "type": "RasterLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 38.612326230162665, + "longitude": -119.77357975468912, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 5.161762963106246 + }, + "sources": { + "5fe556bc-9938-4217-8de6-fe25aef088c2": { + "name": "OpenStreetMap.Mapnik", + "parameters": { + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19.0, + "minZoom": 0.0, + "provider": "OpenStreetMap", + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "urlParameters": {} + }, + "type": "RasterSource" + }, + "d07cc573-51fb-4ae8-965b-a0082ace7f2b": { + "name": "Custom GeoJSON Layer Source", + "parameters": { + "path": "eq.json" + }, + "type": "GeoJSONSource" + } + }, + "terrain": { + "exaggeration": 0.0, + "source": "" + } +} \ No newline at end of file diff --git a/examples/earthquakes.jGIS b/examples/earthquakes.jGIS index bef04c70..d4b04fb7 100644 --- a/examples/earthquakes.jGIS +++ b/examples/earthquakes.jGIS @@ -10,14 +10,14 @@ { "feature": "mag", "operator": ">", - "value": 2.47 + "value": 6.5 } ], "logicalOp": "all" }, "name": "Custom GeoJSON Layer", "parameters": { - "color": "#865e3c", + "color": "#000000", "opacity": 1.0, "source": "d07cc573-51fb-4ae8-965b-a0082ace7f2b", "type": "circle" @@ -36,10 +36,11 @@ }, "options": { "bearing": 0.0, - "latitude": 39.19439624539356, - "longitude": -121.55020132126549, + "latitude": 25.012589761592906, + "longitude": -109.44110652310296, "pitch": 0.0, - "zoom": 4.630124252560623 + "projection": "EPSG:3857", + "zoom": 3.843213728870569 }, "sources": { "5fe556bc-9938-4217-8de6-fe25aef088c2": { diff --git a/examples/france_hiking.jGIS b/examples/france_hiking.jGIS index 65a65936..c448cb60 100644 --- a/examples/france_hiking.jGIS +++ b/examples/france_hiking.jGIS @@ -1,87 +1,89 @@ { + "layerTree": [ + "4a0703b3-ed56-4158-8a2e-e008c3d0fee2", + "7db81237-a579-4daa-938f-5e61fdfb17e7", + "0bfee293-9e2f-4434-8c5a-c90d19836bab" + ], "layers": { - "7db81237-a579-4daa-938f-5e61fdfb17e7": { - "name": "NASAGIBS.ModisTerraTrueColorCR Layer", - "visible": true, - "parameters": { - "source": "52252f5d-3cb7-45a8-a724-5793bf9950ec", - "opacity": 0.3 - }, - "type": "RasterLayer" - }, "0bfee293-9e2f-4434-8c5a-c90d19836bab": { "name": "WaymarkedTrails.hiking Layer", - "type": "RasterLayer", "parameters": { "opacity": 0.6, "source": "82691e55-f9e2-43be-8a07-3ae0409af7b4" }, + "type": "RasterLayer", "visible": true }, "4a0703b3-ed56-4158-8a2e-e008c3d0fee2": { "name": "OpenStreetMap.Mapnik Layer", - "visible": true, - "type": "RasterLayer", "parameters": { "source": "60da082e-8b70-4fa2-b2f0-48524468fea0" - } + }, + "type": "RasterLayer", + "visible": true + }, + "7db81237-a579-4daa-938f-5e61fdfb17e7": { + "name": "NASAGIBS.ModisTerraTrueColorCR Layer", + "parameters": { + "opacity": 0.3, + "source": "52252f5d-3cb7-45a8-a724-5793bf9950ec" + }, + "type": "RasterLayer", + "visible": true } }, + "options": { + "bearing": 0.0, + "latitude": 46.623742146769416, + "longitude": 1.6082511087276998, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 4.947275971927249 + }, "sources": { - "60da082e-8b70-4fa2-b2f0-48524468fea0": { - "type": "RasterSource", - "parameters": { - "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - "minZoom": 0.0, - "maxZoom": 19.0, - "urlParameters": {}, - "provider": "OpenStreetMap", - "attribution": "(C) OpenStreetMap contributors" - }, - "name": "OpenStreetMap.Mapnik" - }, "52252f5d-3cb7-45a8-a724-5793bf9950ec": { + "name": "NASAGIBS.ModisTerraTrueColorCR", "parameters": { + "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", + "maxZoom": 9.0, + "minZoom": 1.0, + "provider": "NASAGIBS", + "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", "urlParameters": { - "time": "2024-07-07", - "tilematrixset": "GoogleMapsCompatible_Level", "format": "jpg", + "tilematrixset": "GoogleMapsCompatible_Level", + "time": "2024-07-07", "variant": "MODIS_Terra_CorrectedReflectance_TrueColor" - }, - "url": "https://map1.vis.earthdata.nasa.gov/wmts-webmerc/{variant}/default/{time}/{tilematrixset}{max_zoom}/{z}/{y}/{x}.{format}", - "minZoom": 1.0, - "maxZoom": 9.0, - "attribution": "Imagery provided by services from the Global Imagery Browse Services (GIBS), operated by the NASA/GSFC/Earth Science Data and Information System (ESDIS) with funding provided by NASA/HQ.", - "provider": "NASAGIBS" + } + }, + "type": "RasterSource" + }, + "60da082e-8b70-4fa2-b2f0-48524468fea0": { + "name": "OpenStreetMap.Mapnik", + "parameters": { + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19.0, + "minZoom": 0.0, + "provider": "OpenStreetMap", + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "urlParameters": {} }, - "name": "NASAGIBS.ModisTerraTrueColorCR", "type": "RasterSource" }, "82691e55-f9e2-43be-8a07-3ae0409af7b4": { "name": "WaymarkedTrails.hiking", - "type": "RasterSource", "parameters": { - "minZoom": 0.0, - "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "attribution": "Map data: (C) OpenStreetMap contributors | Map style: (C) waymarkedtrails.org (CC-BY-SA)", "maxZoom": 18.0, + "minZoom": 0.0, "provider": "WaymarkedTrails", + "url": "https://tile.waymarkedtrails.org/{variant}/{z}/{x}/{y}.png", "urlParameters": { "variant": "hiking" } - } + }, + "type": "RasterSource" } }, - "options": { - "latitude": 46.623742146769416, - "zoom": 4.947275971927249, - "longitude": 1.6082511087276998, - "bearing": 0.0, - "pitch": 0.0 - }, - "layerTree": [ - "4a0703b3-ed56-4158-8a2e-e008c3d0fee2", - "7db81237-a579-4daa-938f-5e61fdfb17e7", - "0bfee293-9e2f-4434-8c5a-c90d19836bab" - ] -} + "terrain": {} +} \ No newline at end of file diff --git a/examples/geotiff-2.jGIS b/examples/geotiff-2.jGIS new file mode 100644 index 00000000..bb2172fd --- /dev/null +++ b/examples/geotiff-2.jGIS @@ -0,0 +1,104 @@ +{ + "layerTree": [ + "f75fd646-bc7d-478b-b65b-de34155b8efa" + ], + "layers": { + "f75fd646-bc7d-478b-b65b-de34155b8efa": { + "name": "Custom GeoTiff Layer", + "parameters": { + "color": [ + "interpolate", + [ + "linear" + ], + [ + "/", + [ + "-", + [ + "band", + 2.0 + ], + [ + "band", + 1.0 + ] + ], + [ + "+", + [ + "band", + 2.0 + ], + [ + "band", + 1.0 + ] + ] + ], + -0.2, + [ + 191.0, + 191.0, + 191.0 + ], + 0.0, + [ + 255.0, + 255.0, + 224.0 + ], + 0.2, + [ + 145.0, + 191.0, + 82.0 + ], + 0.4, + [ + 79.0, + 138.0, + 46.0 + ], + 0.6, + [ + 237.0, + 51.0, + 59.0 + ] + ], + "opacity": 1.0, + "source": "8b1d4258-5d46-48da-b466-496d376b593d" + }, + "type": "WebGlLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 0.0, + "longitude": -26.246075613612998, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 1.5294305541461508 + }, + "sources": { + "8b1d4258-5d46-48da-b466-496d376b593d": { + "name": "Custom GeoTiff Source", + "parameters": { + "normalize": false, + "urls": [ + { + "url": "https://s2downloads.eox.at/demo/EOxCloudless/2020/rgbnir/s2cloudless2020-16bits_sinlge-file_z0-4.tif" + } + ], + "wrapX": true + }, + "type": "GeoTiffSource" + } + }, + "terrain": { + "exaggeration": 0.0, + "source": "" + } +} \ No newline at end of file diff --git a/examples/geotiff.jGIS b/examples/geotiff.jGIS new file mode 100644 index 00000000..9d582b14 --- /dev/null +++ b/examples/geotiff.jGIS @@ -0,0 +1,160 @@ +{ + "layerTree": [ + "1b53b998-e6d5-461d-af06-427a0874421e", + "ed0e4142-38fb-4bae-ae3c-c674eccf4bf3" + ], + "layers": { + "1b53b998-e6d5-461d-af06-427a0874421e": { + "name": "OpenStreetMap.Mapnik Layer", + "parameters": { + "source": "06ed2dc2-7d40-4fe3-adc0-325708e65103" + }, + "type": "RasterLayer", + "visible": true + }, + "ed0e4142-38fb-4bae-ae3c-c674eccf4bf3": { + "name": "Custom GeoTiff Layer", + "parameters": { + "color": [ + "interpolate", + [ + "linear" + ], + [ + "/", + [ + "-", + [ + "band", + 2.0 + ], + [ + "band", + 1.0 + ] + ], + [ + "+", + [ + "band", + 2.0 + ], + [ + "band", + 1.0 + ] + ] + ], + -0.2, + [ + 191.0, + 191.0, + 191.0 + ], + 0.0, + [ + 255.0, + 255.0, + 224.0 + ], + 0.2, + [ + 145.0, + 191.0, + 82.0 + ], + 0.4, + [ + 79.0, + 138.0, + 46.0 + ], + 0.6, + [ + 15.0, + 84.0, + 10.0 + ], + -0.2, + [ + 191.0, + 191.0, + 191.0 + ], + 0.0, + [ + 255.0, + 255.0, + 224.0 + ], + 0.2, + [ + 145.0, + 191.0, + 82.0 + ], + 0.4, + [ + 79.0, + 138.0, + 46.0 + ], + 0.6, + [ + 15.0, + 84.0, + 10.0 + ] + ], + "opacity": 1.0, + "source": "a99a3094-ff80-49ca-8607-e2cf3c0c5bda" + }, + "type": "WebGlLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 47.99438557977547, + "longitude": 3.32263195280587, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 8.09680819573482 + }, + "sources": { + "06ed2dc2-7d40-4fe3-adc0-325708e65103": { + "name": "OpenStreetMap.Mapnik", + "parameters": { + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19.0, + "minZoom": 0.0, + "provider": "OpenStreetMap", + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "urlParameters": {} + }, + "type": "RasterSource" + }, + "a99a3094-ff80-49ca-8607-e2cf3c0c5bda": { + "name": "Custom GeoTiff Source", + "parameters": { + "normalize": true, + "urls": [ + { + "max": 10000.0, + "url": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B04.tif" + }, + { + "max": 10000.0, + "url": "https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B08.tif" + } + ], + "wrapX": false + }, + "type": "GeoTiffSource" + } + }, + "terrain": { + "exaggeration": 0.0, + "source": "" + } +} \ No newline at end of file diff --git a/examples/video_test.jGIS b/examples/image_test.jGIS similarity index 58% rename from examples/video_test.jGIS rename to examples/image_test.jGIS index c3d8c7f8..dccc8722 100644 --- a/examples/video_test.jGIS +++ b/examples/image_test.jGIS @@ -1,26 +1,16 @@ { "layerTree": [ "acb76fb7-df6d-41da-8e08-0208a1c82136", - "6234f9f0-1274-445f-aa48-686a578e8cc8", "756cb737-f817-4ba6-b7c0-8da0b97b9778" ], "layers": { - "6234f9f0-1274-445f-aa48-686a578e8cc8": { - "name": "Custom video Layer Layer", - "parameters": { - "opacity": 1.0, - "source": "319c8756-ffee-45eb-8bbc-36a32526a9f6" - }, - "type": "RasterLayer", - "visible": true - }, "756cb737-f817-4ba6-b7c0-8da0b97b9778": { "name": "Custom Image Layer Layer", "parameters": { "opacity": 1.0, "source": "fb9729b8-82c6-48ac-a12b-6343c0e037ae" }, - "type": "RasterLayer", + "type": "ImageLayer", "visible": true }, "acb76fb7-df6d-41da-8e08-0208a1c82136": { @@ -33,41 +23,14 @@ } }, "options": { - "bearing": -96.0, - "latitude": 37.562984, - "longitude": -122.514426, + "bearing": 0.0, + "latitude": 39.583682016837116, + "longitude": -74.5072609634211, "pitch": 0.0, - "zoom": 17.0 + "projection": "EPSG:3857", + "zoom": 4.988635458547391 }, "sources": { - "319c8756-ffee-45eb-8bbc-36a32526a9f6": { - "name": "Custom Video Source", - "parameters": { - "coordinates": [ - [ - -122.51596391201019, - 37.56238816766053 - ], - [ - -122.51467645168304, - 37.56410183312965 - ], - [ - -122.51309394836426, - 37.563391708549425 - ], - [ - -122.51423120498657, - 37.56161849366671 - ] - ], - "urls": [ - "https://static-assets.mapbox.com/mapbox-gl-js/drone.mp4", - "https://static-assets.mapbox.com/mapbox-gl-js/drone.webm" - ] - }, - "type": "VideoSource" - }, "5f7a1edf-1b76-4f82-893f-540d28998b1d": { "name": "OpenStreetMap.Mapnik", "parameters": { diff --git a/examples/pmtiles-raster.jGIS b/examples/pmtiles-raster.jGIS new file mode 100644 index 00000000..3199c9df --- /dev/null +++ b/examples/pmtiles-raster.jGIS @@ -0,0 +1,64 @@ +{ + "layerTree": [ + "3d4563da-904d-4026-a06b-1e8cffbf536f", + "2815540d-70c6-4eed-ba86-51596adf6863" + ], + "layers": { + "2815540d-70c6-4eed-ba86-51596adf6863": { + "name": "Custom Raster Layer", + "parameters": { + "opacity": 0.5, + "source": "d3a5ad67-d6fe-4793-a6c5-dd773d76c745" + }, + "type": "RasterLayer", + "visible": true + }, + "3d4563da-904d-4026-a06b-1e8cffbf536f": { + "name": "OpenStreetMap.Mapnik Layer", + "parameters": { + "source": "d76035d6-9fb2-41db-ad32-ea4d34268dc9" + }, + "type": "RasterLayer", + "visible": true + } + }, + "options": { + "bearing": 0.0, + "latitude": 0.0, + "longitude": 0.0, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 2.2118882945460037 + }, + "sources": { + "d3a5ad67-d6fe-4793-a6c5-dd773d76c745": { + "name": "Custom Raster Layer Source", + "parameters": { + "attribution": "", + "bounds": [], + "htmlAttribution": "", + "maxZoom": 24.0, + "minZoom": 0.0, + "provider": "", + "url": "https://r2-public.protomaps.com/protomaps-sample-datasets/terrarium_z9.pmtiles" + }, + "type": "RasterSource" + }, + "d76035d6-9fb2-41db-ad32-ea4d34268dc9": { + "name": "OpenStreetMap.Mapnik", + "parameters": { + "attribution": "(C) OpenStreetMap contributors", + "maxZoom": 19.0, + "minZoom": 0.0, + "provider": "OpenStreetMap", + "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", + "urlParameters": {} + }, + "type": "RasterSource" + } + }, + "terrain": { + "exaggeration": 0.0, + "source": "" + } +} \ No newline at end of file diff --git a/examples/pmtiles-rasterDem.jGIS b/examples/pmtiles-rasterDem.jGIS deleted file mode 100644 index d0ee5f0d..00000000 --- a/examples/pmtiles-rasterDem.jGIS +++ /dev/null @@ -1,60 +0,0 @@ -{ - "layerTree": [ - "ee4ddf21-31f1-4a71-befd-c7a456628db6", - "e6a856f0-3883-4d10-9e9f-04132b3eb7f1" - ], - "layers": { - "e6a856f0-3883-4d10-9e9f-04132b3eb7f1": { - "name": "Custom Hillshade Layer Layer", - "parameters": { - "shadowColor": "#473B24", - "source": "7b6789c2-74cd-4a74-ad4d-ceef22d1eab8" - }, - "type": "HillshadeLayer", - "visible": true - }, - "ee4ddf21-31f1-4a71-befd-c7a456628db6": { - "name": "OpenStreetMap.Mapnik Layer", - "parameters": { - "source": "d6afefa4-6dcc-4a24-88ad-f7af0f6d76f3" - }, - "type": "RasterLayer", - "visible": true - } - }, - "options": { - "bearing": 0.0, - "latitude": 2.842170943040401e-14, - "longitude": -14.148151338180469, - "pitch": 0.0, - "zoom": 1.2095796395452967 - }, - "sources": { - "7b6789c2-74cd-4a74-ad4d-ceef22d1eab8": { - "name": "PMTile", - "parameters": { - "encoding": "terrarium", - "tileSize": 512.0, - "url": "pmtiles://https://r2-public.protomaps.com/protomaps-sample-datasets/terrarium_z9.pmtiles", - "urlParameters": {} - }, - "type": "RasterDemSource" - }, - "d6afefa4-6dcc-4a24-88ad-f7af0f6d76f3": { - "name": "OpenStreetMap.Mapnik", - "parameters": { - "attribution": "(C) OpenStreetMap contributors", - "maxZoom": 19.0, - "minZoom": 0.0, - "provider": "OpenStreetMap", - "url": "https://tile.openstreetmap.org/{z}/{x}/{y}.png", - "urlParameters": {} - }, - "type": "RasterSource" - } - }, - "terrain": { - "exaggeration": 0.0, - "source": "" - } -} \ No newline at end of file diff --git a/examples/pmtiles-vector.jGIS b/examples/pmtiles-vector.jGIS index f6968fd1..712dea44 100644 --- a/examples/pmtiles-vector.jGIS +++ b/examples/pmtiles-vector.jGIS @@ -1,57 +1,39 @@ { "layerTree": [ - "34e3dbaa-f1df-4add-af4b-8f8bec9bc0c8", - { - "layers": [ - "3c9de1bc-8a95-4913-a3a7-c76ea54146e6", - "2171c5a1-b237-4413-a358-07ea8bcc67d3" - ], - "name": "Firenze" - } + "93077483-0e49-4135-aa1d-244e29e5cf97", + "95523f4c-27ef-42db-b755-a226ca017a94" ], "layers": { - "2171c5a1-b237-4413-a358-07ea8bcc67d3": { - "name": "Roads Layer", - "parameters": { - "color": "#241f31", - "opacity": 1.0, - "source": "c09567a9-2c88-4818-ad40-536e005e9496", - "sourceLayer": "roads", - "type": "line" - }, - "type": "VectorLayer", - "visible": true - }, - "34e3dbaa-f1df-4add-af4b-8f8bec9bc0c8": { + "93077483-0e49-4135-aa1d-244e29e5cf97": { "name": "OpenStreetMap.Mapnik Layer", "parameters": { - "source": "4cc5d853-cb4a-4d05-9560-0c5e7de54fb0" + "source": "c42d25fc-ca10-4447-ba00-ba22b0367739" }, "type": "RasterLayer", "visible": true }, - "3c9de1bc-8a95-4913-a3a7-c76ea54146e6": { - "name": "Land Use", + "95523f4c-27ef-42db-b755-a226ca017a94": { + "name": "Custom Vector Tile Layer", "parameters": { - "color": "#1a5fb4", + "color": "#FF0000", "opacity": 1.0, - "source": "c09567a9-2c88-4818-ad40-536e005e9496", - "sourceLayer": "landuse", - "type": "fill" + "source": "e52daf20-9b57-4a14-9521-70eabbe4cda2", + "type": "line" }, - "type": "VectorLayer", + "type": "VectorTileLayer", "visible": true } }, "options": { "bearing": 0.0, - "latitude": 43.769833024178524, - "longitude": 11.243657520618626, - "pitch": 9.886721549625761, - "zoom": 11.30647314935016 + "latitude": -40.197854210907515, + "longitude": 173.4976730009204, + "pitch": 0.0, + "projection": "EPSG:3857", + "zoom": 5.898552104144127 }, "sources": { - "4cc5d853-cb4a-4d05-9560-0c5e7de54fb0": { + "c42d25fc-ca10-4447-ba00-ba22b0367739": { "name": "OpenStreetMap.Mapnik", "parameters": { "attribution": "(C) OpenStreetMap contributors", @@ -63,19 +45,12 @@ }, "type": "RasterSource" }, - "c09567a9-2c88-4818-ad40-536e005e9496": { - "name": "PMTiles", + "e52daf20-9b57-4a14-9521-70eabbe4cda2": { + "name": "Custom Vector Tile Layer Source", "parameters": { - "bounds": [ - -180.0, - -85.051129, - 180.0, - 85.051129 - ], "maxZoom": 24.0, "minZoom": 0.0, - "scheme": "xyz", - "url": "pmtiles://https://pmtiles.io/protomaps(vector)ODbL_firenze.pmtiles" + "url": "https://r2-public.protomaps.com/protomaps-sample-datasets/nz-buildings-v3.pmtiles" }, "type": "VectorTileSource" } diff --git a/examples/test.jGIS b/examples/test.jGIS index 2a83c5c3..4cd55f29 100644 --- a/examples/test.jGIS +++ b/examples/test.jGIS @@ -1,5 +1,5 @@ { - "layerTree": [ + "layerTree": [ "a0044fd7-f167-445f-b3d1-620a8f94b498", { "layers": [ @@ -55,10 +55,11 @@ }, "options": { "bearing": 0.0, - "latitude": 45.3283890632207, - "longitude": 2.0698449192905173, + "latitude": 45.40509940459734, + "longitude": 1.2354106422355888, "pitch": 0.0, - "zoom": 4.447501895979073 + "projection": "EPSG:3857", + "zoom": 5.044381209731545 }, "sources": { "5fd42e3b-4681-4607-b15d-65c3a3e89b32": { @@ -89,4 +90,4 @@ } }, "terrain": {} -} +} \ No newline at end of file diff --git a/package.json b/package.json index 731af877..40e46f34 100644 --- a/package.json +++ b/package.json @@ -76,6 +76,8 @@ "@fortawesome/fontawesome-svg-core": "^6.5.2", "@fortawesome/free-solid-svg-icons": "^6.5.2", "@fortawesome/react-fontawesome": "latest", + "geojson-vt": "^4.0.2", + "ol": "^10.1.0", "pmtiles": "^3.0.7" } } diff --git a/packages/base/package.json b/packages/base/package.json index 1565a12f..80199890 100644 --- a/packages/base/package.json +++ b/packages/base/package.json @@ -64,7 +64,7 @@ "ajv": "^8.14.0", "d3-color": "^3.1.0", "geojson-schema": "^1.0.5", - "maplibre-gl": "^4.4.1", + "ol-pmtiles": "^0.5.0", "pbf": "^4.0.1", "react": "^18.0.1", "shpjs": "^6.1.0", diff --git a/packages/base/src/commands.ts b/packages/base/src/commands.ts index 1ed48955..63047458 100644 --- a/packages/base/src/commands.ts +++ b/packages/base/src/commands.ts @@ -13,6 +13,7 @@ import { JupyterFrontEnd } from '@jupyterlab/application'; import { WidgetTracker, showErrorMessage } from '@jupyterlab/apputils'; import { ITranslator } from '@jupyterlab/translation'; import { CommandIDs, icons } from './constants'; +import { ZoomColorWidget } from './dialogs/colorExpressionDialog'; import { CreationFormDialog } from './dialogs/formdialog'; import { LayerBrowserWidget } from './dialogs/layerBrowserDialog'; import { TerrainDialogWidget } from './dialogs/terrainDialog'; @@ -43,6 +44,18 @@ export function addCommands( const trans = translator.load('jupyterlab'); const { commands } = app; + commands.addCommand(CommandIDs.colorExpr, { + label: trans.__('Color Expression'), + isEnabled: () => { + return tracker.currentWidget + ? tracker.currentWidget.context.model.sharedModel.editable + : false; + }, + execute: Private.createZoomColorDialog(tracker), + + ...icons.get(CommandIDs.colorExpr) + }); + commands.addCommand(CommandIDs.redo, { label: trans.__('Redo'), isEnabled: () => { @@ -138,7 +151,7 @@ export function addCommands( sourceData: { minZoom: 0, maxZoom: 24 }, layerData: { name: 'Custom Vector Tile Layer' }, sourceType: 'VectorTileSource', - layerType: 'VectorLayer' + layerType: 'VectorTileLayer' }), ...icons.get(CommandIDs.newVectorTileEntry) }); @@ -208,7 +221,7 @@ export function addCommands( }, layerData: { name: 'Custom Image Layer' }, sourceType: 'ImageSource', - layerType: 'RasterLayer' + layerType: 'ImageLayer' }), ...icons.get(CommandIDs.newImageEntry) }); @@ -268,6 +281,39 @@ export function addCommands( ...icons.get(CommandIDs.newShapefileSource) }); + commands.addCommand(CommandIDs.newGeoTiffEntry, { + label: trans.__('New GeoTiff layer'), + isEnabled: () => { + return tracker.currentWidget + ? tracker.currentWidget.context.model.sharedModel.editable + : false; + }, + execute: Private.createEntry({ + tracker, + formSchemaRegistry, + title: 'Create GeoTiff Layer', + createLayer: true, + createSource: true, + sourceData: { + name: 'Custom GeoTiff Source', + urls: [ + { + url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B04.tif', + max: 10000 + }, + { + url: 'https://sentinel-cogs.s3.us-west-2.amazonaws.com/sentinel-s2-l2a-cogs/21/H/UB/2021/9/S2B_21HUB_20210915_0_L2A/B08.tif', + max: 10000 + } + ] + }, + layerData: { name: 'Custom GeoTiff Layer' }, + sourceType: 'GeoTiffSource', + layerType: 'WebGlLayer' + }), + ...icons.get(CommandIDs.newGeoTiffEntry) + }); + /** * SOURCES only commands. */ @@ -449,7 +495,7 @@ export function addCommands( name: 'Custom Vector Layer' }, sourceType: 'VectorTileSource', - layerType: 'VectorLayer' + layerType: 'VectorTileLayer' }), ...icons.get(CommandIDs.newVectorLayer) }); @@ -780,6 +826,23 @@ namespace Private { }; } + export function createZoomColorDialog( + tracker: WidgetTracker + ) { + return async () => { + const current = tracker.currentWidget; + + if (!current) { + return; + } + + const dialog = new ZoomColorWidget({ + context: current.context + }); + await dialog.launch(); + }; + } + export function createEntry({ tracker, formSchemaRegistry, diff --git a/packages/base/src/constants.ts b/packages/base/src/constants.ts index eb805210..41fe27de 100644 --- a/packages/base/src/constants.ts +++ b/packages/base/src/constants.ts @@ -8,6 +8,7 @@ export namespace CommandIDs { export const createNew = 'jupytergis:create-new-jGIS-file'; export const redo = 'jupytergis:redo'; export const undo = 'jupytergis:undo'; + export const colorExpr = 'jupytergis:colorExpr'; // Layers and sources creation commands export const openLayerBrowser = 'jupytergis:openLayerBrowser'; @@ -19,6 +20,7 @@ export namespace CommandIDs { export const newHillshadeEntry = 'jupytergis:newHillshadeEntry'; export const newImageEntry = 'jupytergis:newImageEntry'; export const newVideoEntry = 'jupytergis:newVideoEntry'; + export const newGeoTiffEntry = 'jupytergis:newGeoTiffEntry'; // Sources only commands export const newRasterSource = 'jupytergis:newRasterSource'; @@ -28,6 +30,7 @@ export namespace CommandIDs { export const newImageSource = 'jupytergis:imageSource'; export const newVideoSource = 'jupytergis:videoSource'; export const newShapefileSource = 'jupytergis:shapefileSource'; + export const newGeoTiffSource = 'jupytergis:newGeoTiffSource'; // Layers only commands export const newRasterLayer = 'jupytergis:newRasterLayer'; @@ -36,6 +39,7 @@ export namespace CommandIDs { export const newImageLayer = 'jupytergis:newImageLayer'; export const newVideoLayer = 'jupytergis:newVideoLayer'; export const newShapefileLayer = 'jupytergis:newShapefileLayer'; + export const newWebGlTileLayer = 'jupytergis:newWebGlTileLayer'; // Layer and group actions export const renameLayer = 'jupytergis:renameLayer'; @@ -84,7 +88,9 @@ const iconObject = { [CommandIDs.newImageEntry]: { iconClass: 'fa fa-image' }, [CommandIDs.newVideoEntry]: { iconClass: 'fa fa-video' }, [CommandIDs.newShapefileLayer]: { iconClass: 'fa fa-file' }, - [CommandIDs.newTerrain]: { iconClass: 'fa fa-mountain' } + [CommandIDs.newGeoTiffEntry]: { iconClass: 'fa fa-certificate' }, + [CommandIDs.newTerrain]: { iconClass: 'fa fa-mountain' }, + [CommandIDs.colorExpr]: { iconClass: 'fa fa-brush' } }; /** diff --git a/packages/base/src/dialogs/colorExpressionDialog.tsx b/packages/base/src/dialogs/colorExpressionDialog.tsx new file mode 100644 index 00000000..97d67798 --- /dev/null +++ b/packages/base/src/dialogs/colorExpressionDialog.tsx @@ -0,0 +1,231 @@ +import { IJupyterGISModel, IWebGlLayer } from '@jupytergis/schema'; +import { Dialog } from '@jupyterlab/apputils'; +import { DocumentRegistry } from '@jupyterlab/docregistry'; +import { Button } from '@jupyterlab/ui-components'; +import { PromiseDelegate } from '@lumino/coreutils'; +import { Signal } from '@lumino/signaling'; +import React, { useEffect, useRef, useState } from 'react'; +import StopRow from './components/color-expression/StopRow'; + +interface IZoomColorProps { + context: DocumentRegistry.IContext; + okSignalPromise: PromiseDelegate>; + cancel: () => void; +} + +export interface IStopRow { + value: number; + color: any; +} + +const ZoomColor = ({ context, okSignalPromise, cancel }: IZoomColorProps) => { + const functions = ['interpolate']; + const rowsRef = useRef(); + const selectedLayerRef = useRef(''); + const [selectedFunction, setSelectedFunction] = useState('interpolate'); + const [selectedLayer, setSelectedLayer] = useState(''); + const [stopRows, setStopRows] = useState([]); + + useEffect(() => { + const handleClientStateChanged = () => { + if (!context.model.localState?.selected?.value) { + return; + } + + // TODO: handle multi select better + const currentLayer = Object.keys( + context.model.localState?.selected?.value + )[0]; + + setSelectedLayer(currentLayer); + }; + + // set the layer on initial render + handleClientStateChanged(); + + context.model.clientStateChanged.connect(handleClientStateChanged); + }, []); + + useEffect(() => { + // This it to parse a color object on the layer + selectedLayerRef.current = selectedLayer; + + const layer = context.model.getLayer(selectedLayer); + if (!layer || !layer.parameters?.color) { + return; + } + + const color = layer.parameters.color; + + // If color is a string we don't need to parse + if (typeof color === 'string') { + return; + } + const pairedObjects: IStopRow[] = []; + + // So if it's not a string then it's an array and we parse + // First element is function (ie interpolate) + // Second element is type of interpolation (ie linear) + // Third is ...something idk what, the NVDI for testing + // Fourth and on is value:color pairs + for (let i = 3; i < color.length; i += 2) { + const obj: IStopRow = { + value: color[i], + color: color[i + 1] + }; + pairedObjects.push(obj); + } + + setStopRows(pairedObjects); + }, [selectedLayer]); + + useEffect(() => { + rowsRef.current = stopRows; + }, [stopRows]); + + const handleOk = () => { + const layer = context.model.getLayer(selectedLayer); + // console.log('selectedLayer', selectedLayer); + if (!layer || !layer.parameters) { + return; + } + + const colorExpr: any = [selectedFunction, ['linear']]; + + const nir = ['band', 2]; + + // near-infrared is the first band from above + const red = ['band', 1]; + + const difference = ['-', nir, red]; + const sum = ['+', nir, red]; + + const ndvi = ['/', difference, sum]; + colorExpr.push(ndvi); + + rowsRef.current?.map(stop => { + colorExpr.push(stop.value); + colorExpr.push(stop.color); + }); + + // colorExpr.push(-0.2); // ndvi values <= -0.2 will get the color below + // colorExpr.push([191, 191, 191]); + // colorExpr.push(0); // ndvi values between -0.2 and 0 will get an interpolated color between the one above and the one below + // colorExpr.push([255, 255, 224]); + // colorExpr.push(0.2); + // colorExpr.push([145, 191, 82]); + // colorExpr.push(0.4); + // colorExpr.push([79, 138, 46]); + // colorExpr.push(0.6); + // colorExpr.push([15, 84, 10]); + + (layer.parameters as IWebGlLayer).color = colorExpr; + context.model.sharedModel.updateLayer(selectedLayerRef.current, layer); + cancel(); + }; + + okSignalPromise.promise.then(okSignal => { + okSignal.connect(handleOk); + }); + + const addStopRow = () => { + setStopRows([ + ...stopRows, + { + value: 0, + color: [0, 0, 0] + } + ]); + }; + + return ( +
+
+ + +
+ {/*
Placeholder
*/} +
+
+ Value + Output Value +
+ {stopRows.map((stop, index) => ( + + ))} +
+
+ + {/* */} +
+
+ ); +}; + +export interface IZoomColorOptions { + context: DocumentRegistry.IContext; +} + +export class ZoomColorWidget extends Dialog { + private okSignal: Signal; + + constructor(options: IZoomColorOptions) { + const cancelCallback = () => { + this.resolve(0); + }; + + const okSignalPromise = new PromiseDelegate< + Signal + >(); + + const body = ( + + ); + + super({ title: 'Color Expression', body }); + + this.id = 'jupytergis::zoomzoom'; + + this.okSignal = new Signal(this); + okSignalPromise.resolve(this.okSignal); + } + + resolve(index?: number): void { + if (index === 0) { + super.resolve(index); + } + + if (index === 1) { + this.okSignal.emit(null); + } + } +} + +export default ZoomColor; diff --git a/packages/base/src/dialogs/components/color-expression/StopRow.tsx b/packages/base/src/dialogs/components/color-expression/StopRow.tsx new file mode 100644 index 00000000..04c281c6 --- /dev/null +++ b/packages/base/src/dialogs/components/color-expression/StopRow.tsx @@ -0,0 +1,104 @@ +import { faTrash } from '@fortawesome/free-solid-svg-icons'; +import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +import { Button } from '@jupyterlab/ui-components'; +import React, { useState } from 'react'; +import { IStopRow } from '../../colorExpressionDialog'; + +const StopRow = ({ + index, + value, + outputValue, + stopRows, + setStopRows +}: { + index: number; + value: number; + outputValue: string; + stopRows: IStopRow[]; + setStopRows: any; +}) => { + const [inputZoom, setInputZoom] = useState(10); + const [inputColor, setInputColor] = useState(''); + + const rgbaStringToHex = rgbaStr => { + // Remove the "rgba(" part and close parenthesis + const rgbaParts = rgbaStr.replace('rgba(', '').replace(')', ''); + + // Split the string into individual components + const [r, g, b, a] = rgbaParts + .split(',') + .map(part => parseInt(part.trim())); + + // Convert R, G, B to hexadecimal and ensure they are two digits long + const rHex = r.toString(16).padStart(2, '0'); + const gHex = g.toString(16).padStart(2, '0'); + const bHex = b.toString(16).padStart(2, '0'); + + // Optionally handle alpha channel if needed + // For simplicity, this example ignores the alpha channel + // If you need to include alpha, you could append it after the RGB part, e.g., `return '#' + rHex + gHex + bHex + (a === 1 ? '' : a.toString(16));` + return '#' + rHex + gHex + bHex; + }; + + const rgbArrToHex = rgbArr => { + const hex = rgbArr + .map(val => { + return val.toString(16).padStart(2, '0'); + return hex.length === 1 ? '0' + hex : hex; + }) + .join(''); + + return '#' + hex; + }; + + const hexToRgb = hex => { + const result = /^#?([a-f\d]{2})([a-f\d]{2})([a-f\d]{2})$/i.exec(hex); + const l = result + ? [ + parseInt(result[1], 16), + parseInt(result[2], 16), + parseInt(result[3], 16) + ] + : null; + return l; + }; + + const handleValueChange = event => { + const newRows = [...stopRows]; + stopRows[index].value = event.target.value; + setStopRows(newRows); + setInputZoom(event.target.value); + }; + + const handleColorChange = event => { + const newRows = [...stopRows]; + stopRows[index].color = hexToRgb(event.target.value); + setStopRows(newRows); + setInputColor(event.target.value); + }; + + return ( +
+ + + +
+ ); +}; + +export default StopRow; diff --git a/packages/base/src/formbuilder/objectform/baseform.tsx b/packages/base/src/formbuilder/objectform/baseform.tsx index 8eaa6e18..8c03899b 100644 --- a/packages/base/src/formbuilder/objectform/baseform.tsx +++ b/packages/base/src/formbuilder/objectform/baseform.tsx @@ -1,14 +1,14 @@ import { SchemaForm } from '@deathbeds/jupyterlab-rjsf'; import { MessageLoop } from '@lumino/messaging'; import { Widget } from '@lumino/widgets'; -import { IChangeEvent, ISubmitEvent } from '@rjsf/core'; +import { IChangeEvent, ISubmitEvent, WidgetProps } from '@rjsf/core'; import * as React from 'react'; -import { IDict } from '../../types'; import { IJupyterGISModel } from '@jupytergis/schema'; -import { deepCopy } from '../../tools'; -import { Signal } from '@lumino/signaling'; import { Dialog } from '@jupyterlab/apputils'; +import { Signal } from '@lumino/signaling'; +import { deepCopy } from '../../tools'; +import { IDict } from '../../types'; export interface IBaseFormStates { schema?: IDict; @@ -267,6 +267,7 @@ export class BaseForm extends React.Component {