|
@@ -1,185 +0,0 @@
|
|
|
-// Copyright (c) Jupyter Development Team.
|
|
|
-// Distributed under the terms of the Modified BSD License.
|
|
|
-
|
|
|
-import {
|
|
|
- IKernel
|
|
|
-} from 'jupyter-js-services';
|
|
|
-
|
|
|
-import {
|
|
|
- deepEqual, JSONValue
|
|
|
-} from 'phosphor/lib/algorithm/json';
|
|
|
-
|
|
|
-import {
|
|
|
- Message
|
|
|
-} from 'phosphor/lib/core/messaging';
|
|
|
-
|
|
|
-import {
|
|
|
- Widget, ResizeMessage
|
|
|
-} from 'phosphor/lib/ui/widget';
|
|
|
-
|
|
|
-import {
|
|
|
- ABCWidgetFactory, IDocumentModel, IDocumentContext
|
|
|
-} from '../docregistry';
|
|
|
-
|
|
|
-
|
|
|
-import leaflet = require('leaflet');
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * The class name added to a map widget.
|
|
|
- */
|
|
|
-const MAP_CLASS = 'jp-MapWidget';
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * A widget for maps.
|
|
|
- */
|
|
|
-export
|
|
|
-class MapWidget extends Widget {
|
|
|
- /**
|
|
|
- * Construct a new map widget.
|
|
|
- */
|
|
|
- constructor(context: IDocumentContext<IDocumentModel>) {
|
|
|
- super();
|
|
|
- this._context = context;
|
|
|
- this.node.tabIndex = -1;
|
|
|
- this.addClass(MAP_CLASS);
|
|
|
-
|
|
|
- this._map = leaflet.map(this.node).fitWorld();
|
|
|
- leaflet.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
|
|
|
- attribution : 'Map data (c) <a href="https://openstreetmap.org">OpenStreetMap</a> contributors',
|
|
|
- min_zoom : 0,
|
|
|
- max_zoom : 18,
|
|
|
- }).addTo(this._map);
|
|
|
-
|
|
|
- // Since we keep track of the widget size, we monkeypatch the map
|
|
|
- // to use our information instead of doing a DOM read every time it needs
|
|
|
- // the size info. We can stop monkeypatching when we have a size hint change
|
|
|
- // available from leaflet (cf.
|
|
|
- // https://github.com/Leaflet/Leaflet/issues/4200#issuecomment-233616337 and
|
|
|
- // https://github.com/jupyter/jupyterlab/pull/454#discussion_r71349224)
|
|
|
- this._map.getSize = () => {
|
|
|
- let map: any = this._map;
|
|
|
- if (!map._size || map._sizeChanged) {
|
|
|
- if (this._width < 0 || this._height < 0) {
|
|
|
- return map.prototype.getSize.call(map);
|
|
|
- }
|
|
|
- map._size = new leaflet.Point(this._width, this._height);
|
|
|
- map._sizeChanged = false;
|
|
|
- }
|
|
|
- return map._size.clone();
|
|
|
- };
|
|
|
-
|
|
|
- context.model.contentChanged.connect(() => {
|
|
|
- this.update();
|
|
|
- });
|
|
|
- context.pathChanged.connect(() => {
|
|
|
- this.update();
|
|
|
- });
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Dispose of the resources used by the widget.
|
|
|
- */
|
|
|
- dispose(): void {
|
|
|
- if (this.isDisposed) {
|
|
|
- return;
|
|
|
- }
|
|
|
- this._context = null;
|
|
|
- this._map.remove();
|
|
|
- this._map = null;
|
|
|
- this._geojsonLayer = null;
|
|
|
- super.dispose();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * A message handler invoked on an `'update-request'` message.
|
|
|
- */
|
|
|
- protected onUpdateRequest(msg: Message): void {
|
|
|
- this.title.label = this._context.path.split('/').pop();
|
|
|
- if (!this.isAttached) {
|
|
|
- return;
|
|
|
- }
|
|
|
- let content = this._context.model.toString();
|
|
|
- let geojson: JSONValue = content ? JSON.parse(content) : content;
|
|
|
- if (deepEqual(geojson, this._geojson)) {
|
|
|
- return;
|
|
|
- }
|
|
|
-
|
|
|
- // we're attached to the DOM and have new layer content now
|
|
|
- if (this._geojsonLayer) {
|
|
|
- this._map.removeLayer(this._geojsonLayer);
|
|
|
- }
|
|
|
- this._geojson = geojson;
|
|
|
- this._geojsonLayer = null;
|
|
|
- if (geojson) {
|
|
|
- this._geojsonLayer = leaflet.geoJson(geojson, {
|
|
|
- pointToLayer: function (feature, latlng) {
|
|
|
- return leaflet.circleMarker(latlng);
|
|
|
- }
|
|
|
- });
|
|
|
- this._map.addLayer(this._geojsonLayer);
|
|
|
- this._fitLayerBounds();
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * A message handler invoked on an `'after-attach'` message.
|
|
|
- */
|
|
|
- protected onAfterAttach(msg: Message): void {
|
|
|
- this.update();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * A message handler invoked on an `'activate-request'` message.
|
|
|
- */
|
|
|
- protected onActivateRequest(msg: Message): void {
|
|
|
- this.node.focus();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * A message handler invoked on a `'resize'` message.
|
|
|
- */
|
|
|
- protected onResize(msg: ResizeMessage) {
|
|
|
- this._sized = true;
|
|
|
- this._width = msg.width;
|
|
|
- this._height = msg.height;
|
|
|
- this._map.invalidateSize(true);
|
|
|
- this._fitLayerBounds();
|
|
|
- }
|
|
|
-
|
|
|
- /**
|
|
|
- * Make the map fit the geojson layer bounds only once when all info is available.
|
|
|
- */
|
|
|
- private _fitLayerBounds() {
|
|
|
- if (!this._fitted && this._sized && this._geojsonLayer) {
|
|
|
- this._map.fitBounds(this._geojsonLayer.getBounds());
|
|
|
- this._fitted = true;
|
|
|
- }
|
|
|
- }
|
|
|
-
|
|
|
- private _fitted = false;
|
|
|
- private _sized = false;
|
|
|
- private _width = -1;
|
|
|
- private _height = -1;
|
|
|
- private _geojson: JSONValue = null;
|
|
|
- private _geojsonLayer: leaflet.GeoJSON;
|
|
|
- private _map: leaflet.Map;
|
|
|
- private _context: IDocumentContext<IDocumentModel>;
|
|
|
-}
|
|
|
-
|
|
|
-
|
|
|
-/**
|
|
|
- * A widget factory for maps.
|
|
|
- */
|
|
|
-export
|
|
|
-class MapWidgetFactory extends ABCWidgetFactory<MapWidget, IDocumentModel> {
|
|
|
- /**
|
|
|
- * Create a new widget given a context.
|
|
|
- */
|
|
|
- createNew(context: IDocumentContext<IDocumentModel>, kernel?: IKernel.IModel): MapWidget {
|
|
|
- let widget = new MapWidget(context);
|
|
|
- this.widgetCreated.emit(widget);
|
|
|
- return widget;
|
|
|
- }
|
|
|
-}
|