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

[WIP] Replace leaflet with mapbox-gl #122

Open
wants to merge 5 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 4 additions & 2 deletions packages/geojson-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
"@jupyterlab/rendermime-interfaces": "^1.0.3",
"@phosphor/messaging": "^1.2.2",
"@phosphor/widgets": "^1.5.0",
"leaflet": "^1.2.0"
"@turf/bbox": "^6.0.1",
"leaflet": "^1.2.0",
"mapbox-gl": "^0.44.2"
},
"devDependencies": {
"@types/leaflet": "^1.2.0",
"@types/mapbox-gl": "^0.44.1",
"rimraf": "~2.6.2",
"typescript": "~2.6.2"
},
Expand Down
125 changes: 52 additions & 73 deletions packages/geojson-extension/src/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,14 @@ import {
IRenderMime
} from '@jupyterlab/rendermime-interfaces';

import * as leaflet from 'leaflet';
import mapboxgl = require('mapbox-gl');

import bbox from '@turf/bbox';

import 'leaflet/dist/leaflet.css';

import '../style/index.css';

import * as iconRetinaUrl from 'leaflet/dist/images/marker-icon-2x.png';
import * as iconUrl from 'leaflet/dist/images/marker-icon.png';
import * as shadowUrl from 'leaflet/dist/images/marker-shadow.png';

/**
* The CSS class to add to the GeoJSON Widget.
Expand All @@ -39,39 +38,7 @@ const CSS_ICON_CLASS = 'jp-MaterialIcon jp-GeoJSONIcon';
export
const MIME_TYPE = 'application/geo+json';

/**
* Set base path for leaflet images.
*/

// https://github.com/Leaflet/Leaflet/issues/4968
// Marker file names are hard-coded in the leaflet source causing
// issues with webpack.
// This workaround allows webpack to inline all marker URLs.

delete (leaflet.Icon.Default.prototype as any)['_getIconUrl'];

leaflet.Icon.Default.mergeOptions({
iconRetinaUrl: iconRetinaUrl,
iconUrl: iconUrl,
shadowUrl: shadowUrl
});


/**
* The url template that leaflet tile layers.
* See http://leafletjs.com/reference-1.0.3.html#tilelayer
*/
const URL_TEMPLATE: string = 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';

/**
* The options for leaflet tile layers.
* See http://leafletjs.com/reference-1.0.3.html#tilelayer
*/
const LAYER_OPTIONS: leaflet.TileLayerOptions = {
attribution: 'Map data (c) <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',
minZoom: 0,
maxZoom: 18
};
mapboxgl.accessToken = 'pk.eyJ1IjoibWlja3QiLCJhIjoiLXJIRS1NbyJ9.EfVT76g4A5dyuApW_zuIFQ';


export
Expand All @@ -82,14 +49,7 @@ class RenderedGeoJSON extends Widget implements IRenderMime.IRenderer {
constructor(options: IRenderMime.IRendererOptions) {
super();
this.addClass(CSS_CLASS);
this._mimeType = options.mimeType;
// Create leaflet map object
// trackResize option set to false as it is not needed to track
// window.resize events since we have individual phosphor resize
// events.
this._map = leaflet.map(this.node, {
trackResize: false
});
this._mimeType = options.mimeType;
}

/**
Expand All @@ -107,37 +67,56 @@ class RenderedGeoJSON extends Widget implements IRenderMime.IRenderer {
*/
renderModel(model: IRenderMime.IMimeModel): Promise<void> {
const data = model.data[this._mimeType] as any | GeoJSON.GeoJsonObject;
const metadata = model.metadata[this._mimeType] as any || {};
// const metadata = model.metadata[this._mimeType] as any || {};
this._map = new mapboxgl.Map({
container: this.node,
style: 'mapbox://styles/mapbox/streets-v9?optimize=true',
zoom: 10
});
return new Promise<void>((resolve, reject) => {
// Add leaflet tile layer to map
leaflet.tileLayer(
metadata.url_template || URL_TEMPLATE,
metadata.layer_options || LAYER_OPTIONS
).addTo(this._map);
// Create GeoJSON layer from data and add to map
this._geoJSONLayer = leaflet.geoJSON(data).addTo(this._map);
this.update();
resolve();
// Add GeoJSON layer to map
this._map.on('style.load', () => {
this._map.addSource('geojson', {
type: 'geojson',
data
});
const [minX, minY, maxX, maxY] = bbox(data);
this._map.fitBounds([[minX, minY], [maxX, maxY]], { maxZoom: 15, padding: 100 });
this._map.addLayer({
id: 'geojson-points',
type: 'circle',
source: 'geojson',
paint: {
'circle-color': 'red',
'circle-stroke-color': 'white',
'circle-stroke-width': { stops: [[0,0.1], [18,3]], base: 1.2 },
'circle-radius': { stops: [[15,3], [18,5]], base: 1.2 }
}
});
this.update();
resolve();
});
});
}

/**
* A message handler invoked on an `'after-attach'` message.
*/
protected onAfterAttach(msg: Message): void {
if (this.parent.hasClass('jp-OutputArea-child')) {
// Disable scroll zoom by default to avoid conflicts with notebook scroll
this._map.scrollWheelZoom.disable();
// Enable scroll zoom on map focus
this._map.on('blur', (event) => {
this._map.scrollWheelZoom.disable();
});
// Disable scroll zoom on blur
this._map.on('focus', (event) => {
this._map.scrollWheelZoom.enable();
});
}
this.update();
// If in a notebook context
// if (this.parent.hasClass('jp-OutputArea-child')) {
// // Disable scroll zoom by default to avoid conflicts with notebook scroll
// this._map.scrollZoom.disable();
// // Enable scroll zoom on map focus
// this._map.on('blur', (event: Event) => {
// this._map.scrollZoom.disable();
// });
// // Disable scroll zoom on blur
// this._map.on('focus', (event: Event) => {
// this._map.scrollZoom.enable();
// });
// }
// this.update();
}

/**
Expand All @@ -159,13 +138,13 @@ class RenderedGeoJSON extends Widget implements IRenderMime.IRenderer {
*/
protected onUpdateRequest(msg: Message): void {
// Update map size after update
if (this.isVisible) this._map.invalidateSize();
// Update map size after panel/window is resized
this._map.fitBounds(this._geoJSONLayer.getBounds());
if (this._map && this.isVisible) {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This will happen automatically in GL JS - the map container will resize based on the current div size of the notebook container. It can be removed

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested this and it appears that it doesn't work automatically. One unique thing about phosphorjs (which is what provides JupyterLab it's Widgets among many other things) is that it can detect resize events on a node-level, so that when a panel is resized within lab, the affected widgets can handle those resizes themselves.

this._map.resize();
}
}

private _map: leaflet.Map;
private _geoJSONLayer: leaflet.GeoJSON;
private _map: mapboxgl.Map;
// private _geoJSONLayer: mapboxgl.GeoJSONSource;
private _mimeType: string;
}

Expand Down
59 changes: 56 additions & 3 deletions yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,23 @@
d3-collection "1"
d3-interpolate "1"

"@turf/bbox@^6.0.1":
version "6.0.1"
resolved "https://registry.npmjs.org/@turf/bbox/-/bbox-6.0.1.tgz#b966075771475940ee1c16be2a12cf389e6e923a"
dependencies:
"@turf/helpers" "6.x"
"@turf/meta" "6.x"

"@turf/[email protected]":
version "6.1.3"
resolved "https://registry.npmjs.org/@turf/helpers/-/helpers-6.1.3.tgz#0001a5c4a3bff25b4bbbc64f8713a383c9ab9e94"

"@turf/[email protected]":
version "6.0.1"
resolved "https://registry.npmjs.org/@turf/meta/-/meta-6.0.1.tgz#cf6f3f2263a3d24fc8d6a7e90f0420bbc44c090d"
dependencies:
"@turf/helpers" "6.x"

"@types/d3-array@*":
version "1.2.1"
resolved "https://registry.npmjs.org/@types/d3-array/-/d3-array-1.2.1.tgz#e489605208d46a1c9d980d2e5772fa9c75d9ec65"
Expand Down Expand Up @@ -503,9 +520,9 @@
version "0.5.0"
resolved "https://registry.npmjs.org/@types/katex/-/katex-0.5.0.tgz#a7dbb981f1bc89c8b1b5e7fa8a1ce2617ab2f358"

"@types/leaflet@^1.2.0":
version "1.2.5"
resolved "https://registry.npmjs.org/@types/leaflet/-/leaflet-1.2.5.tgz#b172502c298849cadd034314dabfc936beff7636"
"@types/mapbox-gl@^0.44.1":
version "0.44.1"
resolved "https://registry.npmjs.org/@types/mapbox-gl/-/mapbox-gl-0.44.1.tgz#6d8618ab2a28e8b78ce2cca403f28a227c9f217d"
dependencies:
"@types/geojson" "*"

Expand Down Expand Up @@ -3439,6 +3456,42 @@ mapbox-gl@^0.44.0:
vt-pbf "^3.0.1"
webworkify "^1.5.0"

mapbox-gl@^0.44.2:
version "0.44.2"
resolved "https://registry.npmjs.org/mapbox-gl/-/mapbox-gl-0.44.2.tgz#8c118ba8c5c15b054272644f30877309db0f8ee2"
dependencies:
"@mapbox/gl-matrix" "^0.0.1"
"@mapbox/mapbox-gl-supported" "^1.3.0"
"@mapbox/point-geometry" "^0.1.0"
"@mapbox/shelf-pack" "^3.1.0"
"@mapbox/tiny-sdf" "^1.1.0"
"@mapbox/unitbezier" "^0.0.0"
"@mapbox/vector-tile" "^1.3.0"
"@mapbox/whoots-js" "^3.0.0"
brfs "^1.4.0"
bubleify "^0.7.0"
csscolorparser "~1.0.2"
earcut "^2.1.3"
geojson-rewind "^0.3.0"
geojson-vt "^3.0.0"
gray-matter "^3.0.8"
grid-index "^1.0.0"
jsonlint-lines-primitives "~1.6.0"
minimist "0.0.8"
package-json-versionify "^1.0.2"
pbf "^3.0.5"
quickselect "^1.0.0"
rw "^1.3.3"
shuffle-seed "^1.1.6"
sort-object "^0.3.2"
supercluster "^2.3.0"
through2 "^2.0.3"
tinyqueue "^1.1.0"
unassertify "^2.0.0"
unflowify "^1.0.0"
vt-pbf "^3.0.1"
webworkify "^1.5.0"

marching-simplex-table@^1.0.0:
version "1.0.0"
resolved "https://registry.npmjs.org/marching-simplex-table/-/marching-simplex-table-1.0.0.tgz#bc16256e0f8f9b558aa9b2872f8832d9433f52ea"
Expand Down