Skip to content

Commit

Permalink
Add raster layer gallery (#16)
Browse files Browse the repository at this point in the history
* Add raster layer gallery

* Add layout browser to toolbar

* Create layer browser

* Add raster layer gallery

* Change thumbnail to use string

* Use real gallery instead of dummy data

* Add layer to map on click

* Add atrribution

* Add search icon

* Add display for added layers

* Rework JSON

* Create categories

* Use shared model as source of layers on map

* Start working on category tabs

* Fix up provider category css

* Category filter wip

* Filter by category

* Add more attributes to raste source json

* Add some attributes to source - wip

* Add new layers to layer tree

* Move layer browser css

* Make layer browser a plugin

* Add webpack-env types to clear ts error

* Move layer browser css

* Create thumbnails at build time and add build:dev option to skip it

* Fix build stuff

* Add script dependecies to build action

* Remove layer tree stuff

* Add some tests

* Small fixes

* Add remove/clear methods to registry

* Add script dependecies to requirements

* Fix tests

* Rename registry functions

* Remove raster layer gallery

* Make attribution optional

* Use models addLayer()

* Add some jsdoc

* CI test

* CI fix maybe

* Rework tests

* Resize grid for smaller windows

---------

Co-authored-by: Greg <[email protected]>
  • Loading branch information
martinRenou and gjmooney authored Jul 2, 2024
1 parent b9d1c78 commit 86c9504
Show file tree
Hide file tree
Showing 27 changed files with 1,220 additions and 19 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ jobs:
python=3.9
jupyterlab=4
yarn=3
pillow
mercantile
xyzservices
- name: Setup pip cache
uses: actions/cache@v2
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/check-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@ jobs:
yarn=3
pip
jupyter-releaser
pillow
mercantile
xyzservices
- name: Check Release
uses: jupyter-server/jupyter_releaser/.github/actions/check-release@v2
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -129,5 +129,6 @@ jupytergis/_version.py

python/jupytergis_lab/jupytergis_lab/labextension/
python/jupytergis_lab/jupytergis_lab/notebook/objects/_schema
packages/base/rasterlayer_gallery/*

untitled*
7 changes: 7 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@
"build": "lerna run build",
"build:prod": "lerna run build:prod",
"build:test": "lerna run build:test",
"build:dev": "lerna run build:dev",
"bump:js:version": "lerna version --no-push --force-publish --no-git-tag-version --yes",
"clean": "lerna run clean",
"clean:all": "lerna run clean:all",
Expand All @@ -56,6 +57,7 @@
"@jupyterlab/services": " ^7.0.0"
},
"devDependencies": {
"@types/webpack-env": "^1.18.5",
"@typescript-eslint/eslint-plugin": "5.55.0",
"@typescript-eslint/parser": "5.55.0",
"copy-webpack-plugin": "^10.0.0",
Expand All @@ -68,5 +70,10 @@
"rimraf": "^3.0.2",
"typescript": "^5",
"webpack": "^5.76.3"
},
"dependencies": {
"@fortawesome/fontawesome-svg-core": "^6.5.2",
"@fortawesome/free-solid-svg-icons": "^6.5.2",
"@fortawesome/react-fontawesome": "latest"
}
}
4 changes: 3 additions & 1 deletion packages/base/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,10 @@
"url": "https://github.com/QuantStack/jupytergis.git"
},
"scripts": {
"build": "tsc -b",
"build": "jlpm run build:gallery && tsc -b",
"build:gallery": "python rasterlayer_gallery_generator.py",
"build:prod": "jlpm run clean && jlpm run build",
"build:dev": "tsc -b",
"clean": "rimraf tsconfig.tsbuildinfo",
"clean:lib": "rimraf lib tsconfig.tsbuildinfo",
"clean:all": "jlpm run clean:lib",
Expand Down
238 changes: 238 additions & 0 deletions packages/base/rasterlayer_gallery_generator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,238 @@
from datetime import date, timedelta
import json
from io import BytesIO
import os

import requests
from PIL import Image
import mercantile
from xyzservices import providers

THUMBNAILS_LOCATION = "rasterlayer_gallery"


def fetch_tile(url_template, x, y, z, s='a'):
"""
Fetch a tile from the given URL template.
"""
url = url_template.format(x=x, y=y, z=z, s=s)
print(f' Fetch {url}')
response = requests.get(url, headers={
"Content-Type": "application/json",
"User-Agent": "JupyterGIS"
})
response.raise_for_status()
return Image.open(BytesIO(response.content))

def latlng_to_tile(lat, lng, zoom):
"""
Convert latitude/longitude to tile coordinates.
"""
tile = mercantile.tile(lng, lat, zoom, True)
return tile.x, tile.y

def create_thumbnail(url_template, lat, lng, zoom, tile_size=256, thumbnail_size=(512, 512)):
"""
Create a thumbnail for the specified location and zoom level.
"""
x, y = latlng_to_tile(lat, lng, zoom)

# Fetch the tiles (2x2 grid for the thumbnail)
tiles = []
for dy in range(2):
row = []
for dx in range(2):
tile_x, tile_y = x + dx, y + dy
tile = fetch_tile(url_template, tile_x, tile_y, zoom)
row.append(tile)
tiles.append(row)

# Create a blank image for the thumbnail
thumbnail = Image.new('RGB', (2 * tile_size, 2 * tile_size))

# Paste the tiles into the thumbnail image
for dy, row in enumerate(tiles):
for dx, tile in enumerate(row):
thumbnail.paste(tile, (dx * tile_size, dy * tile_size))

# Resize to the desired thumbnail size
thumbnail = thumbnail.resize(thumbnail_size, Image.LANCZOS)
return thumbnail


yesterday = (date.today() - timedelta(days=1)).strftime("%Y-%m-%d")

# San Francisco
san_francisco = {
'lat': 37.7749,
'lng': -122.4194,
'zoom': 5
}

middle_europe = {
'lat': 48.63290858589535,
'lng': -350.068359375,
'zoom': 4
}

# Default
france = {
'lat': 47.040182144806664,
'lng': 1.2963867187500002,
'zoom': 5
}

thumbnails_providers_positions = {
'OpenStreetMap': {
'Special Rules': {
'BZH': {
'lat': 47.76702233051035,
'lng': -3.4675598144531254,
'zoom': 8
},
'CH': {
'lat': 46.8182,
'lng': 8.2275,
'zoom': 8
},
'DE': {
'lat': 51.1657,
'lng': 10.4515,
'zoom': 8
},
'France': france,
'HOT': france
},
'Default': france
},
'NASAGIBS': {
'Special Rules': {},
'Default': france
},
# 'JusticeMap': {
# 'Special Rules': {},
# 'Default': san_francisco,
# },
'USGS': {
'Special Rules': {},
'Default': san_francisco,
},
'WaymarkedTrails': {
'Special Rules': {},
'Default': france,
},
'Gaode': {
'Special Rules': {},
'Default': san_francisco,
},
'Strava': {
'Special Rules': {},
'Default': france,
'TileSize': 512
},
'OPNVKarte': {
'Special Rules': {},
'Default': san_francisco,
},
'OpenTopoMap': {
'Special Rules': {},
'Default': san_francisco,
},
'OpenRailwayMap': {
'Special Rules': {},
'Default': san_francisco,
'TileSize': 512
},
# 'OpenFireMap': {
# 'Special Rules': {},
# 'Default': san_francisco,
# },
# 'SafeCast': {
# 'Special Rules': {},
# 'Default': san_francisco,
# }
}

def download_thumbnail(url_template, name, position, tile_size):
file_path = f'{THUMBNAILS_LOCATION}/{name}.png'
thumbnail = create_thumbnail(
url_template,
position['lat'],
position['lng'],
position['zoom'],
tile_size
)
thumbnail.save(file_path)
return file_path


# This is the JSON we'll generate for the raster gallery
raster_provider_gallery = {}

# Create thumbnail dir if needed
if not os.path.exists(THUMBNAILS_LOCATION):
os.makedirs(THUMBNAILS_LOCATION)

# Fetch thumbnails and populate the dictionary
for provider in thumbnails_providers_positions.keys():
xyzprovider = providers[provider]

if 'url' in xyzprovider.keys():
print(f"Process {provider}")

name = provider
url_template = xyzprovider["url"]

if name in thumbnails_providers_positions[provider]['Special Rules'].keys():
position = thumbnails_providers_positions[provider]['Special Rules'][name]
else:
position = thumbnails_providers_positions[provider]['Default']

tile_size = thumbnails_providers_positions[provider].get('TileSize', 256)

# file_path = download_thumbnail(url_template, name, position, tile_size)
raster_provider_gallery[name] = dict(
# jgisname=name,
thumbnailPath='file_path',
**xyzprovider
)

continue

providers_maps = {}
for map_name in xyzprovider.keys():
print(f"Process {provider} {map_name}")


try:
if map_name in thumbnails_providers_positions[provider]['Special Rules'].keys():
position = thumbnails_providers_positions[provider]['Special Rules'][map_name]
else:
position = thumbnails_providers_positions[provider]['Default']

tile_provider = xyzprovider[map_name]

if 'crs' in tile_provider or 'apikey' in tile_provider:
# TODO Support other projections once we have another viewer than maplibre
# TODO Support api keys
continue

name = tile_provider["name"].replace(".", "-")
url_template = tile_provider.build_url(time=yesterday)
tile_size = thumbnails_providers_positions[provider].get('TileSize', 256)

# file_path = download_thumbnail(url_template, name, position, tile_size)
providers_maps[map_name] = dict(
# jgisname=name,
thumbnailPath='file_path',
**tile_provider
)

raster_provider_gallery[provider] = providers_maps

except Exception as e:
print('Failed...', e)

# Save JSON repr
with open(f'{THUMBNAILS_LOCATION}/raster_layer_gallery.json', 'w') as f:
json.dump(raster_provider_gallery, f)
Loading

0 comments on commit 86c9504

Please sign in to comment.