From 831b9042a8c1ae94a7a339b58a58a2a65a8cc4b3 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Wed, 31 Jul 2024 10:19:57 +0200 Subject: [PATCH 1/9] draft swipe position --- examples/RasterLayer.ipynb | 81 +++++++++++++++++++++++++--- src/rastertilelayer.ts | 57 +++++++++++++++++--- src/widget.ts | 106 ++++++++++++++++++++++++++++++++++++- 3 files changed, 228 insertions(+), 16 deletions(-) diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index d054f2b..7d810c9 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -30,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 10, "metadata": { "scrolled": true }, @@ -38,7 +38,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "bf54f7b270eb4c12ba2fbcaeea2583da", + "model_id": "77010bf2973f41d9a93c400f283857bd", "version_major": 2, "version_minor": 0 }, @@ -46,7 +46,7 @@ "Map(center=[4.299875503991089, 46.85012303279379], zoom=0.0)" ] }, - "execution_count": 3, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -65,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -74,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -94,6 +94,64 @@ "m.add_layer(raster) " ] }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f2b5931a98ee4e40b8b23f7e5f3ee8a1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "IntSlider(value=0, description='Swipe Position:')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "77010bf2973f41d9a93c400f283857bd", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[4.299875503991089, 46.85012303279379], layers=[RasterTileLayer(), RasterTileLayer(url='https://api…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "# Création du slider\n", + "slider = widgets.IntSlider(\n", + " value=0,\n", + " min=0,\n", + " max=100,\n", + " step=1,\n", + " description='Swipe Position:',\n", + " continuous_update=True\n", + ")\n", + "\n", + "# Créez une fonction pour mettre à jour le modèle lorsque le slider change\n", + "def on_slider_change(change):\n", + " m.swipe_position=change['new']\n", + "\n", + "# Lier la fonction de mise à jour au changement de valeur du slider\n", + "slider.observe(on_slider_change, names='value')\n", + "\n", + "# Affichez le slider dans le notebook\n", + "display(slider)\n", + "display(m)\n" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -132,6 +190,13 @@ "source": [ "m.remove_layer(rasterlay)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/src/rastertilelayer.ts b/src/rastertilelayer.ts index 68c934f..5fecd76 100644 --- a/src/rastertilelayer.ts +++ b/src/rastertilelayer.ts @@ -1,12 +1,14 @@ -// Copyright (c) QuantStack -// Distributed under the terms of the Modified BSD License. import { DOMWidgetModel, ISerializers } from '@jupyter-widgets/base'; -import TileLayer from 'ol/layer/WebGLTile.js'; +import WebGLTileLayer from 'ol/layer/WebGLTile.js'; import XYZ from 'ol/source/XYZ.js'; import { MODULE_NAME, MODULE_VERSION } from './version'; import { MapView } from './widget'; import { LayerModel, LayerView } from './layer'; +type WebGLEvent = { + context: WebGLRenderingContext; +}; + export class RasterTileLayerModel extends LayerModel { defaults() { return { @@ -28,7 +30,7 @@ export class RasterTileLayerModel extends LayerModel { static serializers: ISerializers = { ...DOMWidgetModel.serializers, - // Add any extra serializers here + // Add any extra serializers ici }; static model_name = 'RasterTileLayerModel'; @@ -41,15 +43,36 @@ export class RasterTileLayerModel extends LayerModel { export class RasterTileLayerView extends LayerView { map_view: MapView; + tileLayer: WebGLTileLayer; + + private prerenderListener: (event: WebGLEvent) => void; + private postrenderListener: (event: WebGLEvent) => void; + private previousSwipePosition: number | undefined; + + constructor(options: any) { + super(options); + this.map_view = options.options.map_view; + this.prerenderListener = this.map_view.handlePrerender.bind(this.map_view); + this.postrenderListener = this.map_view.handlePostrender.bind( + this.map_view, + ); + this.previousSwipePosition = undefined; + } render() { super.render(); this.urlChanged(); this.model.on('change:url', this.urlChanged, this); + this.model.on( + 'change:swipe_position', + this.handleSwipePositionChanged, + this, + ); + this.updateEventListeners(); } create_obj() { - this.obj = this.tileLayer = new TileLayer({ + this.obj = this.tileLayer = new WebGLTileLayer({ source: new XYZ({ url: this.model.get('url'), attributions: this.model.get('attributions'), @@ -74,5 +97,27 @@ export class RasterTileLayerView extends LayerView { } } - tileLayer: TileLayer; + handleSwipePositionChanged() { + const swipePosition = this.model.get('swipe_position'); + console.log('Swipe Position Changed:', swipePosition); + + if (this.previousSwipePosition !== swipePosition) { + this.previousSwipePosition = swipePosition; + this.updateEventListeners(); + this.map_view.map.render(); + } + } + + updateEventListeners() { + console.log('Updating event listeners'); + const swipePosition = this.model.get('swipe_position'); + (this.tileLayer as any).un('precompose', this.prerenderListener); + (this.tileLayer as any).un('postcompose', this.postrenderListener); + + if (swipePosition >= 0) { + (this.tileLayer as any).on('precompose', this.prerenderListener); + (this.tileLayer as any).on('postcompose', this.postrenderListener); + } + console.log('Event listeners updated'); + } } diff --git a/src/widget.ts b/src/widget.ts index 20f3298..90c98f0 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -8,6 +8,8 @@ import { ViewList, } from '@jupyter-widgets/base'; import { LayerModel, LayerView } from './layer'; +import { RasterTileLayerView } from './rastertilelayer'; + import { BaseOverlayModel, BaseOverlayView } from './baseoverlay'; import { BaseControlModel, BaseControlView } from './basecontrol'; import { ViewObjectEventTypes } from 'ol/View'; @@ -31,7 +33,9 @@ export * from './heatmap'; export * from './rastertilelayer'; export * from './geotifflayer'; export * from './vectortilelayer'; - +type WebGLEvent = { + context: WebGLRenderingContext; +}; const DEFAULT_LOCATION = [0.0, 0.0]; export class MapModel extends DOMWidgetModel { @@ -49,6 +53,7 @@ export class MapModel extends DOMWidgetModel { overlays: [], zoom: 2, center: DEFAULT_LOCATION, + swipe_position: 0, }; } @@ -132,12 +137,44 @@ export class MapView extends DOMWidgetView { this.layersChanged(); this.overlayChanged(); this.controlChanged(); + this.handleSwipePositionChanged(); this.model.on('change:layers', this.layersChanged, this); this.model.on('change:overlays', this.overlayChanged, this); this.model.on('change:controls', this.controlChanged, this); this.model.on('change:zoom', this.zoomChanged, this); this.model.on('change:center', this.centerChanged, this); + this.model.on( + 'change:swipe_position', + this.handleSwipePositionChanged, + this, + ); + } + handleSwipePositionChanged() { + const swipePosition = this.model.get('swipe_position'); + console.log('Swipe Position Changed:', swipePosition); + this.updateEventListeners(); + this.map.render(); + } + + async updateEventListeners() { + const layers = this.model.get('layers') as LayerModel[]; + for (const layerModel of layers) { + const layerView = await this.findLayerView(layerModel); + if (layerView) { + layerView.updateEventListeners(); + } + } + } + + async findLayerView( + layerModel: LayerModel, + ): Promise { + const views = await Promise.all(this.layerViews.views); + return views.find((view: LayerView) => view.model === layerModel) as + | RasterTileLayerView + | undefined; } + layersChanged() { const layers = this.model.get('layers') as LayerModel[]; this.layerViews.update(layers); @@ -159,6 +196,10 @@ export class MapView extends DOMWidgetView { this.map.getView().setZoom(newZoom); } } + getSize() { + const size = this.map.getSize(); + return size; + } centerChanged() { const newCenter = this.model.get('center'); @@ -184,17 +225,77 @@ export class MapView extends DOMWidgetView { child_view.remove(); } + handlePrerender(event: WebGLEvent) { + console.log('handlePrerender triggered'); + const gl = event.context; + + if (!gl) { + console.error('WebGL context is undefined'); + return; + } + + try { + // Enable the scissor test + gl.enable(gl.SCISSOR_TEST); + console.log('Scissor test enabled'); + + const mapSize = this.map.getSize(); + if (!mapSize) { + console.error('Map size is undefined'); + return; + } + + const swipePosition = this.model.get('swipe_position') || 0; + const bottomLeft = [0, mapSize[1]]; + const topRight = [mapSize[0], 0]; + + // Calculate width and height for scissor box + const width = Math.max( + 0, + Math.round((topRight[0] - bottomLeft[0]) * (swipePosition / 100)), + ); + const height = Math.max(0, topRight[1] - bottomLeft[1]); + + // Log values for debugging + console.log( + `Scissor box - bottomLeft: ${bottomLeft}, topRight: ${topRight}, width: ${width}, height: ${height}`, + ); + + // Set the scissor box + gl.scissor(bottomLeft[0], bottomLeft[1], width, height); + } catch (error) { + console.error( + 'Error enabling scissor test or setting scissor box:', + error, + ); + } + } + + handlePostrender(event: WebGLEvent) { + console.log('handlePostrender triggered'); + console.log('Event object:', event); + + const gl = event.context; + console.log('GL context:', gl); + gl.disable(gl.SCISSOR_TEST); + } + async addLayerModel(child_model: LayerModel) { const view = await this.create_child_view(child_model, { map_view: this, }); + console.log('add'); + console.log(view.obj); this.map.addLayer(view.obj); + console.log(view); + console.log(this.map); this.displayed.then(() => { view.trigger('displayed', this); }); + console.log(view); + return view; } - async addOverlayModel(child_model: BaseOverlayModel) { const view = await this.create_child_view(child_model, { map_view: this, @@ -224,6 +325,7 @@ export class MapView extends DOMWidgetView { imageElement: HTMLImageElement; map_container: HTMLDivElement; map: Map; + map_view: MapView; layerViews: ViewList; overlayViews: ViewList; controlViews: ViewList; From 0c21a18c1a95d90ed3bd13d4f259765ef4a05484 Mon Sep 17 00:00:00 2001 From: martinRenou Date: Wed, 31 Jul 2024 15:47:14 +0200 Subject: [PATCH 2/9] Release 0.1.0 --- pyproject.toml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pyproject.toml b/pyproject.toml index 70e3887..0a58d87 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -36,8 +36,9 @@ classifiers = [ ] dependencies = [ "ipywidgets>=8.0.0", + "traitlets", ] -version = "0.1.0.dev0" +version = "0.1.0" [project.optional-dependencies] docs = [ From 55ad7b9ab237a961c12090a9a2bab8b0c8d7a65f Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Fri, 2 Aug 2024 10:08:38 +0200 Subject: [PATCH 3/9] add as a control --- css/widget.css | 15 ++++ examples/RasterLayer.ipynb | 152 ++++++++++++++++++++++++++++++++----- ipyopenlayers/Map.py | 18 +++-- src/rastertilelayer.ts | 19 ++--- src/splitcontrol.ts | 129 +++++++++++++++++++++++++++++++ src/splitmapcontrol.ts | 84 ++++++++++++++++++++ src/widget.ts | 102 ++----------------------- 7 files changed, 393 insertions(+), 126 deletions(-) create mode 100644 src/splitcontrol.ts create mode 100644 src/splitmapcontrol.ts diff --git a/css/widget.css b/css/widget.css index 85bb861..2641c91 100644 --- a/css/widget.css +++ b/css/widget.css @@ -19,3 +19,18 @@ overflow: hidden; flex: 1 1 auto; } + +.swiper-container { + position: absolute; + top: 10px; + left: 10px; + z-index: 1000; + background: rgba(255, 255, 255, 0.8); + padding: 5px; + border-radius: 4px; +} + +.swipe { + width: 100%; + margin: 0; +} diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index 7d810c9..f4822ec 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -9,16 +9,16 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "from ipyopenlayers import Map, RasterTileLayer" + "from ipyopenlayers import Map, RasterTileLayer,SplitMapControl" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -30,29 +30,27 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "scrolled": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "77010bf2973f41d9a93c400f283857bd", + "model_id": "4c4c19b373ab45dcb40b6f11831de316", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Map(center=[4.299875503991089, 46.85012303279379], zoom=0.0)" + "Map(center=[0.0, 0.0])" ] }, - "execution_count": 10, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "m = Map(center=[4.299875503991089, 46.85012303279379], zoom=0)\n", + "m = Map()\n", "m" ] }, @@ -65,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -74,7 +72,16 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "layer_b=RasterTileLayer(url='https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +90,34 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "split = SplitMapControl(left_layer=layere, right_layer=layer_b)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "m.add_control(split)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "m.remove_control(split)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -96,13 +130,13 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f2b5931a98ee4e40b8b23f7e5f3ee8a1", + "model_id": "80cf95e1693944cbb9ec94cf2ee3d66f", "version_major": 2, "version_minor": 0 }, @@ -116,7 +150,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "77010bf2973f41d9a93c400f283857bd", + "model_id": "deea77e51560434c83c56da7ca11ad68", "version_major": 2, "version_minor": 0 }, @@ -152,6 +186,83 @@ "display(m)\n" ] }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f80524698f8141e4a398dec0b0db53aa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[0, 0], layers=[RasterTileLayer(), RasterTileLayer(url='https://api.maptiler.com/maps/dataviz-dark/…" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Importer les modules nécessaires\n", + "import ipywidgets as widgets\n", + "from IPython.display import display\n", + "\n", + "map_widget = Map(center=[0, 0], zoom=2)\n", + "left_layer = RasterTileLayer(url='https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png')\n", + "right_layer = RasterTileLayer(url='https://api.maptiler.com/maps/dataviz-dark/{z}/{x}/{y}.png?key=' + key)\n", + "\n", + "# Ajouter les couches au widget de la carte\n", + "map_widget.add_layer(left_layer)\n", + "map_widget.add_layer(right_layer)\n", + "map_widget\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "ename": "TraitError", + "evalue": "The 'layers' trait of a Map instance contains an Instance of a List which expected a Layer, not the SplitMapControl SplitMapControl(left_layer=None, right_layer=None).", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTraitError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[10], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m m \u001b[38;5;241m=\u001b[39m Map(center\u001b[38;5;241m=\u001b[39m[\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m0\u001b[39m], zoom\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m7\u001b[39m)\n\u001b[1;32m 2\u001b[0m control \u001b[38;5;241m=\u001b[39m SplitMapControl()\n\u001b[0;32m----> 3\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madd_layer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcontrol\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Ipy-openlayers/ipyopenlayers/Map.py:147\u001b[0m, in \u001b[0;36mMap.add_layer\u001b[0;34m(self, layer)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21madd_layer\u001b[39m(\u001b[38;5;28mself\u001b[39m, layer):\n\u001b[0;32m--> 147\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlayers\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlayers \u001b[38;5;241m+\u001b[39m [layer]\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:716\u001b[0m, in \u001b[0;36mTraitType.__set__\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 714\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mread_only:\n\u001b[1;32m 715\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TraitError(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mThe \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m trait is read-only.\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m%\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname)\n\u001b[0;32m--> 716\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3635\u001b[0m, in \u001b[0;36mList.set\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3633\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mset(obj, [value]) \u001b[38;5;66;03m# type:ignore[list-item]\u001b[39;00m\n\u001b[1;32m 3634\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 3635\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:690\u001b[0m, in \u001b[0;36mTraitType.set\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 689\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mset\u001b[39m(\u001b[38;5;28mself\u001b[39m, obj: HasTraits, value: S) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 690\u001b[0m new_value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 691\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 692\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:722\u001b[0m, in \u001b[0;36mTraitType._validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[1;32m 721\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalidate\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 722\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_cross_validation_lock \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m 724\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_cross_validate(obj, value)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3482\u001b[0m, in \u001b[0;36mContainer.validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3479\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 3480\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[0;32m-> 3482\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3484\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m t\u001b[38;5;241m.\u001b[39mcast(T, value)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3629\u001b[0m, in \u001b[0;36mList.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3626\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m length \u001b[38;5;241m<\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_minlen \u001b[38;5;129;01mor\u001b[39;00m length \u001b[38;5;241m>\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_maxlen:\n\u001b[1;32m 3627\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength_error(obj, value)\n\u001b[0;32m-> 3629\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3494\u001b[0m, in \u001b[0;36mContainer.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3492\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_trait\u001b[38;5;241m.\u001b[39m_validate(obj, v)\n\u001b[1;32m 3493\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TraitError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[0;32m-> 3494\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3495\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 3496\u001b[0m validated\u001b[38;5;241m.\u001b[39mappend(v)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:810\u001b[0m, in \u001b[0;36mTraitType.error\u001b[0;34m(self, obj, value, error, info)\u001b[0m\n\u001b[1;32m 801\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 802\u001b[0m error\u001b[38;5;241m.\u001b[39margs \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 803\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m trait contains \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m which \u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexpected \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m, not \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(\n\u001b[1;32m 804\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 808\u001b[0m ),\n\u001b[1;32m 809\u001b[0m )\n\u001b[0;32m--> 810\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m error\n\u001b[1;32m 812\u001b[0m \u001b[38;5;66;03m# this trait caused an error\u001b[39;00m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# this is not the root trait\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3492\u001b[0m, in \u001b[0;36mContainer.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3490\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m v \u001b[38;5;129;01min\u001b[39;00m value:\n\u001b[1;32m 3491\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 3492\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_trait\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3493\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TraitError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[1;32m 3494\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39merror(obj, v, error)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:722\u001b[0m, in \u001b[0;36mTraitType._validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[1;32m 721\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalidate\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 722\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_cross_validation_lock \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m 724\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_cross_validate(obj, value)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:2311\u001b[0m, in \u001b[0;36mInstance.validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 2309\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m t\u001b[38;5;241m.\u001b[39mcast(T, value)\n\u001b[1;32m 2310\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 2311\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:815\u001b[0m, in \u001b[0;36mTraitType.error\u001b[0;34m(self, obj, value, error, info)\u001b[0m\n\u001b[1;32m 812\u001b[0m \u001b[38;5;66;03m# this trait caused an error\u001b[39;00m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# this is not the root trait\u001b[39;00m\n\u001b[0;32m--> 815\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TraitError(value, info \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minfo(), \u001b[38;5;28mself\u001b[39m)\n\u001b[1;32m 817\u001b[0m \u001b[38;5;66;03m# this is the root trait\u001b[39;00m\n\u001b[1;32m 818\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "\u001b[0;31mTraitError\u001b[0m: The 'layers' trait of a Map instance contains an Instance of a List which expected a Layer, not the SplitMapControl SplitMapControl(left_layer=None, right_layer=None)." + ] + } + ], + "source": [ + "m = Map(center=[0,0], zoom=7)\n", + "control = SplitMapControl()\n", + "m.add_layer(control)" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -191,6 +302,13 @@ "m.remove_layer(rasterlay)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, diff --git a/ipyopenlayers/Map.py b/ipyopenlayers/Map.py index d17b0ec..79733f8 100644 --- a/ipyopenlayers/Map.py +++ b/ipyopenlayers/Map.py @@ -67,9 +67,6 @@ class HeatmapLayer(Layer): blur =Int(15).tag(sync=True) radius = Int(8).tag(sync=True) - - - class BaseOverlay(DOMWidget): _model_module = Unicode(module_name).tag(sync=True) @@ -121,6 +118,14 @@ class MousePosition(BaseControl): _view_name = Unicode('MousePositionView').tag(sync=True) _model_name = Unicode('MousePositionModel').tag(sync=True) +class SplitMapControl(BaseControl): + _model_name = Unicode('SplitMapControlModel').tag(sync=True) + _view_name = Unicode('SplitMapControlView').tag(sync=True) + left_layer = Instance(Layer).tag(sync=True, **widget_serialization) + right_layer = Instance(Layer).tag(sync=True, **widget_serialization) + swipe_position = Int(50).tag(sync=True) + + class Map(DOMWidget): _model_name = Unicode('MapModel').tag(sync=True) @@ -137,7 +142,6 @@ class Map(DOMWidget): controls=List(Instance(BaseControl)).tag(sync=True, **widget_serialization) - def __init__(self, center=None, zoom=None, **kwargs): super().__init__(**kwargs) @@ -166,4 +170,8 @@ def remove_control(self, control): def clear_layers(self): - self.layers = [] \ No newline at end of file + self.layers = [] + + + + \ No newline at end of file diff --git a/src/rastertilelayer.ts b/src/rastertilelayer.ts index 5fecd76..3726a31 100644 --- a/src/rastertilelayer.ts +++ b/src/rastertilelayer.ts @@ -5,10 +5,11 @@ import { MODULE_NAME, MODULE_VERSION } from './version'; import { MapView } from './widget'; import { LayerModel, LayerView } from './layer'; +/* type WebGLEvent = { context: WebGLRenderingContext; }; - +*/ export class RasterTileLayerModel extends LayerModel { defaults() { return { @@ -44,12 +45,12 @@ export class RasterTileLayerModel extends LayerModel { export class RasterTileLayerView extends LayerView { map_view: MapView; tileLayer: WebGLTileLayer; - + /* private prerenderListener: (event: WebGLEvent) => void; private postrenderListener: (event: WebGLEvent) => void; private previousSwipePosition: number | undefined; - constructor(options: any) { + constructor(options: any) { super(options); this.map_view = options.options.map_view; this.prerenderListener = this.map_view.handlePrerender.bind(this.map_view); @@ -57,18 +58,18 @@ export class RasterTileLayerView extends LayerView { this.map_view, ); this.previousSwipePosition = undefined; - } + }*/ render() { super.render(); this.urlChanged(); this.model.on('change:url', this.urlChanged, this); - this.model.on( + /*this.model.on( 'change:swipe_position', this.handleSwipePositionChanged, this, - ); - this.updateEventListeners(); + );*/ + //this.updateEventListeners(); } create_obj() { @@ -97,7 +98,7 @@ export class RasterTileLayerView extends LayerView { } } - handleSwipePositionChanged() { + /*handleSwipePositionChanged() { const swipePosition = this.model.get('swipe_position'); console.log('Swipe Position Changed:', swipePosition); @@ -119,5 +120,5 @@ export class RasterTileLayerView extends LayerView { (this.tileLayer as any).on('postcompose', this.postrenderListener); } console.log('Event listeners updated'); - } + }*/ } diff --git a/src/splitcontrol.ts b/src/splitcontrol.ts new file mode 100644 index 0000000..5aab264 --- /dev/null +++ b/src/splitcontrol.ts @@ -0,0 +1,129 @@ +// Copyright (c) QuantStack +// Distributed under the terms of the Modified BSD License. +import Control from 'ol/control/Control'; +import { getRenderPixel } from 'ol/render'; +import 'ol/ol.css'; +import '../css/widget.css'; +import { MapView } from './widget'; + +// Interface for SplitMapControl options +interface SplitMapControlOptions { + target?: string; + map_view?: MapView; + swipe_position?: number; // Ajoutez swipe_position en tant que nombre +} +export default class SplitMapControl extends Control { + swipe: HTMLInputElement; + leftLayer: any; + rightLayer: any; + map_view: MapView; + swipe_position: any; + constructor( + leftLayer: any, + rightLayer: any, + options: SplitMapControlOptions = {}, + ) { + const element = document.createElement('div'); + element.className = 'ol-unselectable ol-control split-map-control'; + + super({ + element: element, + target: options.target, + }); + + this.leftLayer = leftLayer; + this.rightLayer = rightLayer; + + console.log('Initializing SplitMapControl...'); + console.log(options); + if (options.map_view) { + this.map_view = options.map_view; + console.log('MapView initialized:', this.map_view); + } else { + throw new Error('MapView is required for SplitMapControl.'); + } + if (options.swipe_position) { + this.swipe_position = options.swipe_position; + console.log('swipe_position initialized:', this.swipe_position); + } else { + throw new Error('swipe_position is required for SplitMapControl.'); + } + + const swiperContainer = document.createElement('div'); + swiperContainer.className = 'swiper-container'; + + this.swipe = document.createElement('input'); + this.swipe.type = 'range'; + this.swipe.className = 'swipe'; + (this.swipe.value = this.swipe_position), + swiperContainer.appendChild(this.swipe); + console.log('this.map_view.map_container', this.map_view.map_container); + this.map_view.map_container.appendChild(swiperContainer); + this.handleSwipe(); + } + + handleSwipe(event?: Event) { + console.log('Handling swipe event...'); + console.log(this.swipe.value); + if (!this.leftLayer.hasListener('prerender')) { + console.log('1'); + this.leftLayer.on('prerender', this.prerender.bind(this)); + } + + if (!this.leftLayer.hasListener('postrender')) { + console.log('2'); + //this.leftLayer.on('postrender', this.postrender.bind(this)); + } + + if (!this.rightLayer.hasListener('prerender')) { + console.log('3'); + + //this.rightLayer.on('prerender', this.prerender.bind(this)); + } + + if (!this.rightLayer.hasListener('postrender')) { + console.log('4'); + + this.rightLayer.on('postrender', this.postrender.bind(this)); + } + + console.log('ok'); + } + + prerender(event: any) { + console.log('Prerender event triggered.'); + + const gl = event.context; // Get WebGL context + gl.enable(gl.SCISSOR_TEST); // Enable scissor test + + const mapSize = this.map_view.getSize(); // Get the size of the map + if (!mapSize) { + console.warn('Map size is undefined.'); + return; + } + + // Get render pixels for the bottom left and top right corners + const bottomLeft = getRenderPixel(event, [0, mapSize[1]]); + const topRight = getRenderPixel(event, [mapSize[0], 0]); + + // Get the swipe value from the input element + const swipeValue = this.swipe.value; + console.log('swipeValue', swipeValue); + + // Calculate the width and height for the scissor test + const width = Math.round( + (topRight[0] - bottomLeft[0]) * (Number(swipeValue) / 100), + ); + const height = topRight[1] - bottomLeft[1]; + + // Define the scissor box + gl.scissor(bottomLeft[0], bottomLeft[1], width, height); + } + + postrender(event: any) { + console.log('Postrender event triggered.'); + + const gl = event.context; // Get WebGL context + gl.disable(gl.SCISSOR_TEST); // Disable scissor test + } +} \ No newline at end of file diff --git a/src/splitmapcontrol.ts b/src/splitmapcontrol.ts new file mode 100644 index 0000000..5a48e0f --- /dev/null +++ b/src/splitmapcontrol.ts @@ -0,0 +1,84 @@ +// Copyright (c) QuantStack +// Distributed under the terms of the Modified BSD License. +import { unpack_models } from '@jupyter-widgets/base'; +import { BaseControlModel, BaseControlView } from './basecontrol'; +import 'ol/ol.css'; +import '../css/widget.css'; +import SplitMapControl from './splitcontrol'; + +// Modèle pour le contrôle de carte divisée +export class SplitMapControlModel extends BaseControlModel { + defaults() { + return { + ...super.defaults(), + _view_name: 'SplitMapControlView', + _model_name: 'SplitMapControlModel', + left_layer: undefined, + right_layer: undefined, + swipe_position: 50, + }; + } +} + +SplitMapControlModel.serializers = { + ...BaseControlModel.serializers, + left_layer: { deserialize: unpack_models }, + right_layer: { deserialize: unpack_models }, +}; + +function asArray(arg: any) { + return Array.isArray(arg) ? arg : [arg]; +} + +export class SplitMapControlView extends BaseControlView { + swipe_position: number; + initialize(parameters: any) { + super.initialize(parameters); + this.map_view = this.options.map_view; + this.map_view.layer_views = this.options.map_view.layerViews; + console.log('hello'); + if (this.map_view && !this.map_view.layerViews) { + console.warn( + 'Layer views is not initialized. Ensure it is properly set.', + ); + } + } + + createObj() { + console.log('Creating SplitMapControl object...'); + const left_models = asArray(this.model.get('left_layer')); + + const right_models = asArray(this.model.get('right_layer')); + + let layersModel = this.map_view.model.get('layers'); + + layersModel = layersModel.concat(left_models, right_models); + + return this.map_view.layer_views.update(layersModel).then((views: any) => { + const left_views: any[] = []; + console.log(left_views); + const right_views: any[] = []; + console.log(right_views); + + views.forEach((view: any) => { + if (left_models.indexOf(view.model) !== -1) { + left_views.push(view.obj); + } + if (right_models.indexOf(view.model) !== -1) { + right_views.push(view.obj); + } + }); + + console.log('Left views:', left_views); + console.log('Right views:', right_views); + this.swipe_position = this.model.get('swipe_position'); + console.log('swipe_position:', this.swipe_position); + + this.obj = new SplitMapControl(left_views[0], right_views[0], { + map_view: this.map_view, + swipe_position: this.swipe_position, + }); + console.log('SplitMapControl created:', this.obj); + }); + } +} diff --git a/src/widget.ts b/src/widget.ts index 90c98f0..baeb9fc 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -8,11 +8,12 @@ import { ViewList, } from '@jupyter-widgets/base'; import { LayerModel, LayerView } from './layer'; -import { RasterTileLayerView } from './rastertilelayer'; - import { BaseOverlayModel, BaseOverlayView } from './baseoverlay'; import { BaseControlModel, BaseControlView } from './basecontrol'; import { ViewObjectEventTypes } from 'ol/View'; +// widget.ts +import { SplitMapControlModel, SplitMapControlView } from './splitmapcontrol'; +export { SplitMapControlModel, SplitMapControlView }; import { Map } from 'ol'; import View from 'ol/View'; @@ -33,9 +34,11 @@ export * from './heatmap'; export * from './rastertilelayer'; export * from './geotifflayer'; export * from './vectortilelayer'; -type WebGLEvent = { +export * from './splitmapcontrol'; + +/*type WebGLEvent = { context: WebGLRenderingContext; -}; +};*/ const DEFAULT_LOCATION = [0.0, 0.0]; export class MapModel extends DOMWidgetModel { @@ -101,7 +104,6 @@ export class MapView extends DOMWidgetView { this.removeLayerView, this, ); - this.overlayViews = new ViewList( this.addOverlayModel, this.removeOverlayView, @@ -137,49 +139,17 @@ export class MapView extends DOMWidgetView { this.layersChanged(); this.overlayChanged(); this.controlChanged(); - this.handleSwipePositionChanged(); this.model.on('change:layers', this.layersChanged, this); this.model.on('change:overlays', this.overlayChanged, this); this.model.on('change:controls', this.controlChanged, this); this.model.on('change:zoom', this.zoomChanged, this); this.model.on('change:center', this.centerChanged, this); - this.model.on( - 'change:swipe_position', - this.handleSwipePositionChanged, - this, - ); - } - handleSwipePositionChanged() { - const swipePosition = this.model.get('swipe_position'); - console.log('Swipe Position Changed:', swipePosition); - this.updateEventListeners(); - this.map.render(); - } - - async updateEventListeners() { - const layers = this.model.get('layers') as LayerModel[]; - for (const layerModel of layers) { - const layerView = await this.findLayerView(layerModel); - if (layerView) { - layerView.updateEventListeners(); - } - } - } - - async findLayerView( - layerModel: LayerModel, - ): Promise { - const views = await Promise.all(this.layerViews.views); - return views.find((view: LayerView) => view.model === layerModel) as - | RasterTileLayerView - | undefined; } layersChanged() { const layers = this.model.get('layers') as LayerModel[]; this.layerViews.update(layers); } - overlayChanged() { const overlay = this.model.get('overlays') as BaseOverlayModel[]; this.overlayViews.update(overlay); @@ -225,69 +195,11 @@ export class MapView extends DOMWidgetView { child_view.remove(); } - handlePrerender(event: WebGLEvent) { - console.log('handlePrerender triggered'); - const gl = event.context; - - if (!gl) { - console.error('WebGL context is undefined'); - return; - } - - try { - // Enable the scissor test - gl.enable(gl.SCISSOR_TEST); - console.log('Scissor test enabled'); - - const mapSize = this.map.getSize(); - if (!mapSize) { - console.error('Map size is undefined'); - return; - } - - const swipePosition = this.model.get('swipe_position') || 0; - const bottomLeft = [0, mapSize[1]]; - const topRight = [mapSize[0], 0]; - - // Calculate width and height for scissor box - const width = Math.max( - 0, - Math.round((topRight[0] - bottomLeft[0]) * (swipePosition / 100)), - ); - const height = Math.max(0, topRight[1] - bottomLeft[1]); - - // Log values for debugging - console.log( - `Scissor box - bottomLeft: ${bottomLeft}, topRight: ${topRight}, width: ${width}, height: ${height}`, - ); - - // Set the scissor box - gl.scissor(bottomLeft[0], bottomLeft[1], width, height); - } catch (error) { - console.error( - 'Error enabling scissor test or setting scissor box:', - error, - ); - } - } - - handlePostrender(event: WebGLEvent) { - console.log('handlePostrender triggered'); - console.log('Event object:', event); - - const gl = event.context; - console.log('GL context:', gl); - gl.disable(gl.SCISSOR_TEST); - } - async addLayerModel(child_model: LayerModel) { const view = await this.create_child_view(child_model, { map_view: this, }); - console.log('add'); - console.log(view.obj); this.map.addLayer(view.obj); - console.log(view); console.log(this.map); this.displayed.then(() => { view.trigger('displayed', this); From 218cb688877ad95e470a8efb3de441ec6ad3bf56 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Wed, 31 Jul 2024 10:19:57 +0200 Subject: [PATCH 4/9] draft swipe position --- examples/RasterLayer.ipynb | 81 +++++++++++++++++++++++++--- src/rastertilelayer.ts | 57 +++++++++++++++++--- src/widget.ts | 106 ++++++++++++++++++++++++++++++++++++- 3 files changed, 228 insertions(+), 16 deletions(-) diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index d054f2b..7d810c9 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -9,7 +9,7 @@ }, { "cell_type": "code", - "execution_count": 1, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -18,7 +18,7 @@ }, { "cell_type": "code", - "execution_count": 2, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -30,7 +30,7 @@ }, { "cell_type": "code", - "execution_count": 3, + "execution_count": 10, "metadata": { "scrolled": true }, @@ -38,7 +38,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "bf54f7b270eb4c12ba2fbcaeea2583da", + "model_id": "77010bf2973f41d9a93c400f283857bd", "version_major": 2, "version_minor": 0 }, @@ -46,7 +46,7 @@ "Map(center=[4.299875503991089, 46.85012303279379], zoom=0.0)" ] }, - "execution_count": 3, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -65,7 +65,7 @@ }, { "cell_type": "code", - "execution_count": 4, + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -74,7 +74,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +83,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 13, "metadata": {}, "outputs": [], "source": [ @@ -94,6 +94,64 @@ "m.add_layer(raster) " ] }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f2b5931a98ee4e40b8b23f7e5f3ee8a1", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "IntSlider(value=0, description='Swipe Position:')" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "77010bf2973f41d9a93c400f283857bd", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[4.299875503991089, 46.85012303279379], layers=[RasterTileLayer(), RasterTileLayer(url='https://api…" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "import ipywidgets as widgets\n", + "# Création du slider\n", + "slider = widgets.IntSlider(\n", + " value=0,\n", + " min=0,\n", + " max=100,\n", + " step=1,\n", + " description='Swipe Position:',\n", + " continuous_update=True\n", + ")\n", + "\n", + "# Créez une fonction pour mettre à jour le modèle lorsque le slider change\n", + "def on_slider_change(change):\n", + " m.swipe_position=change['new']\n", + "\n", + "# Lier la fonction de mise à jour au changement de valeur du slider\n", + "slider.observe(on_slider_change, names='value')\n", + "\n", + "# Affichez le slider dans le notebook\n", + "display(slider)\n", + "display(m)\n" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -132,6 +190,13 @@ "source": [ "m.remove_layer(rasterlay)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/src/rastertilelayer.ts b/src/rastertilelayer.ts index 68c934f..5fecd76 100644 --- a/src/rastertilelayer.ts +++ b/src/rastertilelayer.ts @@ -1,12 +1,14 @@ -// Copyright (c) QuantStack -// Distributed under the terms of the Modified BSD License. import { DOMWidgetModel, ISerializers } from '@jupyter-widgets/base'; -import TileLayer from 'ol/layer/WebGLTile.js'; +import WebGLTileLayer from 'ol/layer/WebGLTile.js'; import XYZ from 'ol/source/XYZ.js'; import { MODULE_NAME, MODULE_VERSION } from './version'; import { MapView } from './widget'; import { LayerModel, LayerView } from './layer'; +type WebGLEvent = { + context: WebGLRenderingContext; +}; + export class RasterTileLayerModel extends LayerModel { defaults() { return { @@ -28,7 +30,7 @@ export class RasterTileLayerModel extends LayerModel { static serializers: ISerializers = { ...DOMWidgetModel.serializers, - // Add any extra serializers here + // Add any extra serializers ici }; static model_name = 'RasterTileLayerModel'; @@ -41,15 +43,36 @@ export class RasterTileLayerModel extends LayerModel { export class RasterTileLayerView extends LayerView { map_view: MapView; + tileLayer: WebGLTileLayer; + + private prerenderListener: (event: WebGLEvent) => void; + private postrenderListener: (event: WebGLEvent) => void; + private previousSwipePosition: number | undefined; + + constructor(options: any) { + super(options); + this.map_view = options.options.map_view; + this.prerenderListener = this.map_view.handlePrerender.bind(this.map_view); + this.postrenderListener = this.map_view.handlePostrender.bind( + this.map_view, + ); + this.previousSwipePosition = undefined; + } render() { super.render(); this.urlChanged(); this.model.on('change:url', this.urlChanged, this); + this.model.on( + 'change:swipe_position', + this.handleSwipePositionChanged, + this, + ); + this.updateEventListeners(); } create_obj() { - this.obj = this.tileLayer = new TileLayer({ + this.obj = this.tileLayer = new WebGLTileLayer({ source: new XYZ({ url: this.model.get('url'), attributions: this.model.get('attributions'), @@ -74,5 +97,27 @@ export class RasterTileLayerView extends LayerView { } } - tileLayer: TileLayer; + handleSwipePositionChanged() { + const swipePosition = this.model.get('swipe_position'); + console.log('Swipe Position Changed:', swipePosition); + + if (this.previousSwipePosition !== swipePosition) { + this.previousSwipePosition = swipePosition; + this.updateEventListeners(); + this.map_view.map.render(); + } + } + + updateEventListeners() { + console.log('Updating event listeners'); + const swipePosition = this.model.get('swipe_position'); + (this.tileLayer as any).un('precompose', this.prerenderListener); + (this.tileLayer as any).un('postcompose', this.postrenderListener); + + if (swipePosition >= 0) { + (this.tileLayer as any).on('precompose', this.prerenderListener); + (this.tileLayer as any).on('postcompose', this.postrenderListener); + } + console.log('Event listeners updated'); + } } diff --git a/src/widget.ts b/src/widget.ts index 20f3298..90c98f0 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -8,6 +8,8 @@ import { ViewList, } from '@jupyter-widgets/base'; import { LayerModel, LayerView } from './layer'; +import { RasterTileLayerView } from './rastertilelayer'; + import { BaseOverlayModel, BaseOverlayView } from './baseoverlay'; import { BaseControlModel, BaseControlView } from './basecontrol'; import { ViewObjectEventTypes } from 'ol/View'; @@ -31,7 +33,9 @@ export * from './heatmap'; export * from './rastertilelayer'; export * from './geotifflayer'; export * from './vectortilelayer'; - +type WebGLEvent = { + context: WebGLRenderingContext; +}; const DEFAULT_LOCATION = [0.0, 0.0]; export class MapModel extends DOMWidgetModel { @@ -49,6 +53,7 @@ export class MapModel extends DOMWidgetModel { overlays: [], zoom: 2, center: DEFAULT_LOCATION, + swipe_position: 0, }; } @@ -132,12 +137,44 @@ export class MapView extends DOMWidgetView { this.layersChanged(); this.overlayChanged(); this.controlChanged(); + this.handleSwipePositionChanged(); this.model.on('change:layers', this.layersChanged, this); this.model.on('change:overlays', this.overlayChanged, this); this.model.on('change:controls', this.controlChanged, this); this.model.on('change:zoom', this.zoomChanged, this); this.model.on('change:center', this.centerChanged, this); + this.model.on( + 'change:swipe_position', + this.handleSwipePositionChanged, + this, + ); + } + handleSwipePositionChanged() { + const swipePosition = this.model.get('swipe_position'); + console.log('Swipe Position Changed:', swipePosition); + this.updateEventListeners(); + this.map.render(); + } + + async updateEventListeners() { + const layers = this.model.get('layers') as LayerModel[]; + for (const layerModel of layers) { + const layerView = await this.findLayerView(layerModel); + if (layerView) { + layerView.updateEventListeners(); + } + } + } + + async findLayerView( + layerModel: LayerModel, + ): Promise { + const views = await Promise.all(this.layerViews.views); + return views.find((view: LayerView) => view.model === layerModel) as + | RasterTileLayerView + | undefined; } + layersChanged() { const layers = this.model.get('layers') as LayerModel[]; this.layerViews.update(layers); @@ -159,6 +196,10 @@ export class MapView extends DOMWidgetView { this.map.getView().setZoom(newZoom); } } + getSize() { + const size = this.map.getSize(); + return size; + } centerChanged() { const newCenter = this.model.get('center'); @@ -184,17 +225,77 @@ export class MapView extends DOMWidgetView { child_view.remove(); } + handlePrerender(event: WebGLEvent) { + console.log('handlePrerender triggered'); + const gl = event.context; + + if (!gl) { + console.error('WebGL context is undefined'); + return; + } + + try { + // Enable the scissor test + gl.enable(gl.SCISSOR_TEST); + console.log('Scissor test enabled'); + + const mapSize = this.map.getSize(); + if (!mapSize) { + console.error('Map size is undefined'); + return; + } + + const swipePosition = this.model.get('swipe_position') || 0; + const bottomLeft = [0, mapSize[1]]; + const topRight = [mapSize[0], 0]; + + // Calculate width and height for scissor box + const width = Math.max( + 0, + Math.round((topRight[0] - bottomLeft[0]) * (swipePosition / 100)), + ); + const height = Math.max(0, topRight[1] - bottomLeft[1]); + + // Log values for debugging + console.log( + `Scissor box - bottomLeft: ${bottomLeft}, topRight: ${topRight}, width: ${width}, height: ${height}`, + ); + + // Set the scissor box + gl.scissor(bottomLeft[0], bottomLeft[1], width, height); + } catch (error) { + console.error( + 'Error enabling scissor test or setting scissor box:', + error, + ); + } + } + + handlePostrender(event: WebGLEvent) { + console.log('handlePostrender triggered'); + console.log('Event object:', event); + + const gl = event.context; + console.log('GL context:', gl); + gl.disable(gl.SCISSOR_TEST); + } + async addLayerModel(child_model: LayerModel) { const view = await this.create_child_view(child_model, { map_view: this, }); + console.log('add'); + console.log(view.obj); this.map.addLayer(view.obj); + console.log(view); + console.log(this.map); this.displayed.then(() => { view.trigger('displayed', this); }); + console.log(view); + return view; } - async addOverlayModel(child_model: BaseOverlayModel) { const view = await this.create_child_view(child_model, { map_view: this, @@ -224,6 +325,7 @@ export class MapView extends DOMWidgetView { imageElement: HTMLImageElement; map_container: HTMLDivElement; map: Map; + map_view: MapView; layerViews: ViewList; overlayViews: ViewList; controlViews: ViewList; From ef1ee7946bfdcd6defaa36b6531c64fc29cd23ec Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Fri, 2 Aug 2024 10:08:38 +0200 Subject: [PATCH 5/9] add as a control --- css/widget.css | 15 ++++ examples/RasterLayer.ipynb | 152 ++++++++++++++++++++++++++++++++----- ipyopenlayers/Map.py | 18 +++-- src/rastertilelayer.ts | 19 ++--- src/splitcontrol.ts | 129 +++++++++++++++++++++++++++++++ src/splitmapcontrol.ts | 84 ++++++++++++++++++++ src/widget.ts | 102 ++----------------------- 7 files changed, 393 insertions(+), 126 deletions(-) create mode 100644 src/splitcontrol.ts create mode 100644 src/splitmapcontrol.ts diff --git a/css/widget.css b/css/widget.css index 85bb861..2641c91 100644 --- a/css/widget.css +++ b/css/widget.css @@ -19,3 +19,18 @@ overflow: hidden; flex: 1 1 auto; } + +.swiper-container { + position: absolute; + top: 10px; + left: 10px; + z-index: 1000; + background: rgba(255, 255, 255, 0.8); + padding: 5px; + border-radius: 4px; +} + +.swipe { + width: 100%; + margin: 0; +} diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index 7d810c9..f4822ec 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -9,16 +9,16 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 1, "metadata": {}, "outputs": [], "source": [ - "from ipyopenlayers import Map, RasterTileLayer" + "from ipyopenlayers import Map, RasterTileLayer,SplitMapControl" ] }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 2, "metadata": {}, "outputs": [], "source": [ @@ -30,29 +30,27 @@ }, { "cell_type": "code", - "execution_count": 10, - "metadata": { - "scrolled": true - }, + "execution_count": 3, + "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "77010bf2973f41d9a93c400f283857bd", + "model_id": "4c4c19b373ab45dcb40b6f11831de316", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Map(center=[4.299875503991089, 46.85012303279379], zoom=0.0)" + "Map(center=[0.0, 0.0])" ] }, - "execution_count": 10, + "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "m = Map(center=[4.299875503991089, 46.85012303279379], zoom=0)\n", + "m = Map()\n", "m" ] }, @@ -65,7 +63,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -74,7 +72,16 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 5, + "metadata": {}, + "outputs": [], + "source": [ + "layer_b=RasterTileLayer(url='https://{a-c}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}.png')" + ] + }, + { + "cell_type": "code", + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -83,7 +90,34 @@ }, { "cell_type": "code", - "execution_count": 13, + "execution_count": 6, + "metadata": {}, + "outputs": [], + "source": [ + "split = SplitMapControl(left_layer=layere, right_layer=layer_b)" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [], + "source": [ + "m.add_control(split)" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [], + "source": [ + "m.remove_control(split)" + ] + }, + { + "cell_type": "code", + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -96,13 +130,13 @@ }, { "cell_type": "code", - "execution_count": 14, + "execution_count": 7, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f2b5931a98ee4e40b8b23f7e5f3ee8a1", + "model_id": "80cf95e1693944cbb9ec94cf2ee3d66f", "version_major": 2, "version_minor": 0 }, @@ -116,7 +150,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "77010bf2973f41d9a93c400f283857bd", + "model_id": "deea77e51560434c83c56da7ca11ad68", "version_major": 2, "version_minor": 0 }, @@ -152,6 +186,83 @@ "display(m)\n" ] }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "f80524698f8141e4a398dec0b0db53aa", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[0, 0], layers=[RasterTileLayer(), RasterTileLayer(url='https://api.maptiler.com/maps/dataviz-dark/…" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Importer les modules nécessaires\n", + "import ipywidgets as widgets\n", + "from IPython.display import display\n", + "\n", + "map_widget = Map(center=[0, 0], zoom=2)\n", + "left_layer = RasterTileLayer(url='https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png')\n", + "right_layer = RasterTileLayer(url='https://api.maptiler.com/maps/dataviz-dark/{z}/{x}/{y}.png?key=' + key)\n", + "\n", + "# Ajouter les couches au widget de la carte\n", + "map_widget.add_layer(left_layer)\n", + "map_widget.add_layer(right_layer)\n", + "map_widget\n" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": { + "collapsed": true, + "jupyter": { + "outputs_hidden": true + } + }, + "outputs": [ + { + "ename": "TraitError", + "evalue": "The 'layers' trait of a Map instance contains an Instance of a List which expected a Layer, not the SplitMapControl SplitMapControl(left_layer=None, right_layer=None).", + "output_type": "error", + "traceback": [ + "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[0;31mTraitError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[0;32mIn[10], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m m \u001b[38;5;241m=\u001b[39m Map(center\u001b[38;5;241m=\u001b[39m[\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m0\u001b[39m], zoom\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m7\u001b[39m)\n\u001b[1;32m 2\u001b[0m control \u001b[38;5;241m=\u001b[39m SplitMapControl()\n\u001b[0;32m----> 3\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madd_layer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcontrol\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/Ipy-openlayers/ipyopenlayers/Map.py:147\u001b[0m, in \u001b[0;36mMap.add_layer\u001b[0;34m(self, layer)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21madd_layer\u001b[39m(\u001b[38;5;28mself\u001b[39m, layer):\n\u001b[0;32m--> 147\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlayers\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlayers \u001b[38;5;241m+\u001b[39m [layer]\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:716\u001b[0m, in \u001b[0;36mTraitType.__set__\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 714\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mread_only:\n\u001b[1;32m 715\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TraitError(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mThe \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m trait is read-only.\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m%\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname)\n\u001b[0;32m--> 716\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3635\u001b[0m, in \u001b[0;36mList.set\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3633\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mset(obj, [value]) \u001b[38;5;66;03m# type:ignore[list-item]\u001b[39;00m\n\u001b[1;32m 3634\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 3635\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:690\u001b[0m, in \u001b[0;36mTraitType.set\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 689\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mset\u001b[39m(\u001b[38;5;28mself\u001b[39m, obj: HasTraits, value: S) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 690\u001b[0m new_value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 691\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 692\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:722\u001b[0m, in \u001b[0;36mTraitType._validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[1;32m 721\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalidate\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 722\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_cross_validation_lock \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m 724\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_cross_validate(obj, value)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3482\u001b[0m, in \u001b[0;36mContainer.validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3479\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 3480\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[0;32m-> 3482\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3484\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m t\u001b[38;5;241m.\u001b[39mcast(T, value)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3629\u001b[0m, in \u001b[0;36mList.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3626\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m length \u001b[38;5;241m<\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_minlen \u001b[38;5;129;01mor\u001b[39;00m length \u001b[38;5;241m>\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_maxlen:\n\u001b[1;32m 3627\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength_error(obj, value)\n\u001b[0;32m-> 3629\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3494\u001b[0m, in \u001b[0;36mContainer.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3492\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_trait\u001b[38;5;241m.\u001b[39m_validate(obj, v)\n\u001b[1;32m 3493\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TraitError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[0;32m-> 3494\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3495\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 3496\u001b[0m validated\u001b[38;5;241m.\u001b[39mappend(v)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:810\u001b[0m, in \u001b[0;36mTraitType.error\u001b[0;34m(self, obj, value, error, info)\u001b[0m\n\u001b[1;32m 801\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 802\u001b[0m error\u001b[38;5;241m.\u001b[39margs \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 803\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m trait contains \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m which \u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexpected \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m, not \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(\n\u001b[1;32m 804\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 808\u001b[0m ),\n\u001b[1;32m 809\u001b[0m )\n\u001b[0;32m--> 810\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m error\n\u001b[1;32m 812\u001b[0m \u001b[38;5;66;03m# this trait caused an error\u001b[39;00m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# this is not the root trait\u001b[39;00m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3492\u001b[0m, in \u001b[0;36mContainer.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3490\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m v \u001b[38;5;129;01min\u001b[39;00m value:\n\u001b[1;32m 3491\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 3492\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_trait\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3493\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TraitError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[1;32m 3494\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39merror(obj, v, error)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:722\u001b[0m, in \u001b[0;36mTraitType._validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[1;32m 721\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalidate\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 722\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_cross_validation_lock \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m 724\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_cross_validate(obj, value)\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:2311\u001b[0m, in \u001b[0;36mInstance.validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 2309\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m t\u001b[38;5;241m.\u001b[39mcast(T, value)\n\u001b[1;32m 2310\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 2311\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:815\u001b[0m, in \u001b[0;36mTraitType.error\u001b[0;34m(self, obj, value, error, info)\u001b[0m\n\u001b[1;32m 812\u001b[0m \u001b[38;5;66;03m# this trait caused an error\u001b[39;00m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# this is not the root trait\u001b[39;00m\n\u001b[0;32m--> 815\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TraitError(value, info \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minfo(), \u001b[38;5;28mself\u001b[39m)\n\u001b[1;32m 817\u001b[0m \u001b[38;5;66;03m# this is the root trait\u001b[39;00m\n\u001b[1;32m 818\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", + "\u001b[0;31mTraitError\u001b[0m: The 'layers' trait of a Map instance contains an Instance of a List which expected a Layer, not the SplitMapControl SplitMapControl(left_layer=None, right_layer=None)." + ] + } + ], + "source": [ + "m = Map(center=[0,0], zoom=7)\n", + "control = SplitMapControl()\n", + "m.add_layer(control)" + ] + }, { "cell_type": "code", "execution_count": 8, @@ -191,6 +302,13 @@ "m.remove_layer(rasterlay)" ] }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] + }, { "cell_type": "code", "execution_count": null, diff --git a/ipyopenlayers/Map.py b/ipyopenlayers/Map.py index d17b0ec..79733f8 100644 --- a/ipyopenlayers/Map.py +++ b/ipyopenlayers/Map.py @@ -67,9 +67,6 @@ class HeatmapLayer(Layer): blur =Int(15).tag(sync=True) radius = Int(8).tag(sync=True) - - - class BaseOverlay(DOMWidget): _model_module = Unicode(module_name).tag(sync=True) @@ -121,6 +118,14 @@ class MousePosition(BaseControl): _view_name = Unicode('MousePositionView').tag(sync=True) _model_name = Unicode('MousePositionModel').tag(sync=True) +class SplitMapControl(BaseControl): + _model_name = Unicode('SplitMapControlModel').tag(sync=True) + _view_name = Unicode('SplitMapControlView').tag(sync=True) + left_layer = Instance(Layer).tag(sync=True, **widget_serialization) + right_layer = Instance(Layer).tag(sync=True, **widget_serialization) + swipe_position = Int(50).tag(sync=True) + + class Map(DOMWidget): _model_name = Unicode('MapModel').tag(sync=True) @@ -137,7 +142,6 @@ class Map(DOMWidget): controls=List(Instance(BaseControl)).tag(sync=True, **widget_serialization) - def __init__(self, center=None, zoom=None, **kwargs): super().__init__(**kwargs) @@ -166,4 +170,8 @@ def remove_control(self, control): def clear_layers(self): - self.layers = [] \ No newline at end of file + self.layers = [] + + + + \ No newline at end of file diff --git a/src/rastertilelayer.ts b/src/rastertilelayer.ts index 5fecd76..3726a31 100644 --- a/src/rastertilelayer.ts +++ b/src/rastertilelayer.ts @@ -5,10 +5,11 @@ import { MODULE_NAME, MODULE_VERSION } from './version'; import { MapView } from './widget'; import { LayerModel, LayerView } from './layer'; +/* type WebGLEvent = { context: WebGLRenderingContext; }; - +*/ export class RasterTileLayerModel extends LayerModel { defaults() { return { @@ -44,12 +45,12 @@ export class RasterTileLayerModel extends LayerModel { export class RasterTileLayerView extends LayerView { map_view: MapView; tileLayer: WebGLTileLayer; - + /* private prerenderListener: (event: WebGLEvent) => void; private postrenderListener: (event: WebGLEvent) => void; private previousSwipePosition: number | undefined; - constructor(options: any) { + constructor(options: any) { super(options); this.map_view = options.options.map_view; this.prerenderListener = this.map_view.handlePrerender.bind(this.map_view); @@ -57,18 +58,18 @@ export class RasterTileLayerView extends LayerView { this.map_view, ); this.previousSwipePosition = undefined; - } + }*/ render() { super.render(); this.urlChanged(); this.model.on('change:url', this.urlChanged, this); - this.model.on( + /*this.model.on( 'change:swipe_position', this.handleSwipePositionChanged, this, - ); - this.updateEventListeners(); + );*/ + //this.updateEventListeners(); } create_obj() { @@ -97,7 +98,7 @@ export class RasterTileLayerView extends LayerView { } } - handleSwipePositionChanged() { + /*handleSwipePositionChanged() { const swipePosition = this.model.get('swipe_position'); console.log('Swipe Position Changed:', swipePosition); @@ -119,5 +120,5 @@ export class RasterTileLayerView extends LayerView { (this.tileLayer as any).on('postcompose', this.postrenderListener); } console.log('Event listeners updated'); - } + }*/ } diff --git a/src/splitcontrol.ts b/src/splitcontrol.ts new file mode 100644 index 0000000..5aab264 --- /dev/null +++ b/src/splitcontrol.ts @@ -0,0 +1,129 @@ +// Copyright (c) QuantStack +// Distributed under the terms of the Modified BSD License. +import Control from 'ol/control/Control'; +import { getRenderPixel } from 'ol/render'; +import 'ol/ol.css'; +import '../css/widget.css'; +import { MapView } from './widget'; + +// Interface for SplitMapControl options +interface SplitMapControlOptions { + target?: string; + map_view?: MapView; + swipe_position?: number; // Ajoutez swipe_position en tant que nombre +} +export default class SplitMapControl extends Control { + swipe: HTMLInputElement; + leftLayer: any; + rightLayer: any; + map_view: MapView; + swipe_position: any; + constructor( + leftLayer: any, + rightLayer: any, + options: SplitMapControlOptions = {}, + ) { + const element = document.createElement('div'); + element.className = 'ol-unselectable ol-control split-map-control'; + + super({ + element: element, + target: options.target, + }); + + this.leftLayer = leftLayer; + this.rightLayer = rightLayer; + + console.log('Initializing SplitMapControl...'); + console.log(options); + if (options.map_view) { + this.map_view = options.map_view; + console.log('MapView initialized:', this.map_view); + } else { + throw new Error('MapView is required for SplitMapControl.'); + } + if (options.swipe_position) { + this.swipe_position = options.swipe_position; + console.log('swipe_position initialized:', this.swipe_position); + } else { + throw new Error('swipe_position is required for SplitMapControl.'); + } + + const swiperContainer = document.createElement('div'); + swiperContainer.className = 'swiper-container'; + + this.swipe = document.createElement('input'); + this.swipe.type = 'range'; + this.swipe.className = 'swipe'; + (this.swipe.value = this.swipe_position), + swiperContainer.appendChild(this.swipe); + console.log('this.map_view.map_container', this.map_view.map_container); + this.map_view.map_container.appendChild(swiperContainer); + this.handleSwipe(); + } + + handleSwipe(event?: Event) { + console.log('Handling swipe event...'); + console.log(this.swipe.value); + if (!this.leftLayer.hasListener('prerender')) { + console.log('1'); + this.leftLayer.on('prerender', this.prerender.bind(this)); + } + + if (!this.leftLayer.hasListener('postrender')) { + console.log('2'); + //this.leftLayer.on('postrender', this.postrender.bind(this)); + } + + if (!this.rightLayer.hasListener('prerender')) { + console.log('3'); + + //this.rightLayer.on('prerender', this.prerender.bind(this)); + } + + if (!this.rightLayer.hasListener('postrender')) { + console.log('4'); + + this.rightLayer.on('postrender', this.postrender.bind(this)); + } + + console.log('ok'); + } + + prerender(event: any) { + console.log('Prerender event triggered.'); + + const gl = event.context; // Get WebGL context + gl.enable(gl.SCISSOR_TEST); // Enable scissor test + + const mapSize = this.map_view.getSize(); // Get the size of the map + if (!mapSize) { + console.warn('Map size is undefined.'); + return; + } + + // Get render pixels for the bottom left and top right corners + const bottomLeft = getRenderPixel(event, [0, mapSize[1]]); + const topRight = getRenderPixel(event, [mapSize[0], 0]); + + // Get the swipe value from the input element + const swipeValue = this.swipe.value; + console.log('swipeValue', swipeValue); + + // Calculate the width and height for the scissor test + const width = Math.round( + (topRight[0] - bottomLeft[0]) * (Number(swipeValue) / 100), + ); + const height = topRight[1] - bottomLeft[1]; + + // Define the scissor box + gl.scissor(bottomLeft[0], bottomLeft[1], width, height); + } + + postrender(event: any) { + console.log('Postrender event triggered.'); + + const gl = event.context; // Get WebGL context + gl.disable(gl.SCISSOR_TEST); // Disable scissor test + } +} \ No newline at end of file diff --git a/src/splitmapcontrol.ts b/src/splitmapcontrol.ts new file mode 100644 index 0000000..5a48e0f --- /dev/null +++ b/src/splitmapcontrol.ts @@ -0,0 +1,84 @@ +// Copyright (c) QuantStack +// Distributed under the terms of the Modified BSD License. +import { unpack_models } from '@jupyter-widgets/base'; +import { BaseControlModel, BaseControlView } from './basecontrol'; +import 'ol/ol.css'; +import '../css/widget.css'; +import SplitMapControl from './splitcontrol'; + +// Modèle pour le contrôle de carte divisée +export class SplitMapControlModel extends BaseControlModel { + defaults() { + return { + ...super.defaults(), + _view_name: 'SplitMapControlView', + _model_name: 'SplitMapControlModel', + left_layer: undefined, + right_layer: undefined, + swipe_position: 50, + }; + } +} + +SplitMapControlModel.serializers = { + ...BaseControlModel.serializers, + left_layer: { deserialize: unpack_models }, + right_layer: { deserialize: unpack_models }, +}; + +function asArray(arg: any) { + return Array.isArray(arg) ? arg : [arg]; +} + +export class SplitMapControlView extends BaseControlView { + swipe_position: number; + initialize(parameters: any) { + super.initialize(parameters); + this.map_view = this.options.map_view; + this.map_view.layer_views = this.options.map_view.layerViews; + console.log('hello'); + if (this.map_view && !this.map_view.layerViews) { + console.warn( + 'Layer views is not initialized. Ensure it is properly set.', + ); + } + } + + createObj() { + console.log('Creating SplitMapControl object...'); + const left_models = asArray(this.model.get('left_layer')); + + const right_models = asArray(this.model.get('right_layer')); + + let layersModel = this.map_view.model.get('layers'); + + layersModel = layersModel.concat(left_models, right_models); + + return this.map_view.layer_views.update(layersModel).then((views: any) => { + const left_views: any[] = []; + console.log(left_views); + const right_views: any[] = []; + console.log(right_views); + + views.forEach((view: any) => { + if (left_models.indexOf(view.model) !== -1) { + left_views.push(view.obj); + } + if (right_models.indexOf(view.model) !== -1) { + right_views.push(view.obj); + } + }); + + console.log('Left views:', left_views); + console.log('Right views:', right_views); + this.swipe_position = this.model.get('swipe_position'); + console.log('swipe_position:', this.swipe_position); + + this.obj = new SplitMapControl(left_views[0], right_views[0], { + map_view: this.map_view, + swipe_position: this.swipe_position, + }); + console.log('SplitMapControl created:', this.obj); + }); + } +} diff --git a/src/widget.ts b/src/widget.ts index 90c98f0..baeb9fc 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -8,11 +8,12 @@ import { ViewList, } from '@jupyter-widgets/base'; import { LayerModel, LayerView } from './layer'; -import { RasterTileLayerView } from './rastertilelayer'; - import { BaseOverlayModel, BaseOverlayView } from './baseoverlay'; import { BaseControlModel, BaseControlView } from './basecontrol'; import { ViewObjectEventTypes } from 'ol/View'; +// widget.ts +import { SplitMapControlModel, SplitMapControlView } from './splitmapcontrol'; +export { SplitMapControlModel, SplitMapControlView }; import { Map } from 'ol'; import View from 'ol/View'; @@ -33,9 +34,11 @@ export * from './heatmap'; export * from './rastertilelayer'; export * from './geotifflayer'; export * from './vectortilelayer'; -type WebGLEvent = { +export * from './splitmapcontrol'; + +/*type WebGLEvent = { context: WebGLRenderingContext; -}; +};*/ const DEFAULT_LOCATION = [0.0, 0.0]; export class MapModel extends DOMWidgetModel { @@ -101,7 +104,6 @@ export class MapView extends DOMWidgetView { this.removeLayerView, this, ); - this.overlayViews = new ViewList( this.addOverlayModel, this.removeOverlayView, @@ -137,49 +139,17 @@ export class MapView extends DOMWidgetView { this.layersChanged(); this.overlayChanged(); this.controlChanged(); - this.handleSwipePositionChanged(); this.model.on('change:layers', this.layersChanged, this); this.model.on('change:overlays', this.overlayChanged, this); this.model.on('change:controls', this.controlChanged, this); this.model.on('change:zoom', this.zoomChanged, this); this.model.on('change:center', this.centerChanged, this); - this.model.on( - 'change:swipe_position', - this.handleSwipePositionChanged, - this, - ); - } - handleSwipePositionChanged() { - const swipePosition = this.model.get('swipe_position'); - console.log('Swipe Position Changed:', swipePosition); - this.updateEventListeners(); - this.map.render(); - } - - async updateEventListeners() { - const layers = this.model.get('layers') as LayerModel[]; - for (const layerModel of layers) { - const layerView = await this.findLayerView(layerModel); - if (layerView) { - layerView.updateEventListeners(); - } - } - } - - async findLayerView( - layerModel: LayerModel, - ): Promise { - const views = await Promise.all(this.layerViews.views); - return views.find((view: LayerView) => view.model === layerModel) as - | RasterTileLayerView - | undefined; } layersChanged() { const layers = this.model.get('layers') as LayerModel[]; this.layerViews.update(layers); } - overlayChanged() { const overlay = this.model.get('overlays') as BaseOverlayModel[]; this.overlayViews.update(overlay); @@ -225,69 +195,11 @@ export class MapView extends DOMWidgetView { child_view.remove(); } - handlePrerender(event: WebGLEvent) { - console.log('handlePrerender triggered'); - const gl = event.context; - - if (!gl) { - console.error('WebGL context is undefined'); - return; - } - - try { - // Enable the scissor test - gl.enable(gl.SCISSOR_TEST); - console.log('Scissor test enabled'); - - const mapSize = this.map.getSize(); - if (!mapSize) { - console.error('Map size is undefined'); - return; - } - - const swipePosition = this.model.get('swipe_position') || 0; - const bottomLeft = [0, mapSize[1]]; - const topRight = [mapSize[0], 0]; - - // Calculate width and height for scissor box - const width = Math.max( - 0, - Math.round((topRight[0] - bottomLeft[0]) * (swipePosition / 100)), - ); - const height = Math.max(0, topRight[1] - bottomLeft[1]); - - // Log values for debugging - console.log( - `Scissor box - bottomLeft: ${bottomLeft}, topRight: ${topRight}, width: ${width}, height: ${height}`, - ); - - // Set the scissor box - gl.scissor(bottomLeft[0], bottomLeft[1], width, height); - } catch (error) { - console.error( - 'Error enabling scissor test or setting scissor box:', - error, - ); - } - } - - handlePostrender(event: WebGLEvent) { - console.log('handlePostrender triggered'); - console.log('Event object:', event); - - const gl = event.context; - console.log('GL context:', gl); - gl.disable(gl.SCISSOR_TEST); - } - async addLayerModel(child_model: LayerModel) { const view = await this.create_child_view(child_model, { map_view: this, }); - console.log('add'); - console.log(view.obj); this.map.addLayer(view.obj); - console.log(view); console.log(this.map); this.displayed.then(() => { view.trigger('displayed', this); From 4628e509b69fad30306232d5c83c5f358b1340a7 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Fri, 2 Aug 2024 10:15:24 +0200 Subject: [PATCH 6/9] linting --- src/splitcontrol.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/splitcontrol.ts b/src/splitcontrol.ts index 5aab264..816e5a5 100644 --- a/src/splitcontrol.ts +++ b/src/splitcontrol.ts @@ -126,4 +126,4 @@ export default class SplitMapControl extends Control { const gl = event.context; // Get WebGL context gl.disable(gl.SCISSOR_TEST); // Disable scissor test } -} \ No newline at end of file +} From bb8679ef3772dc0570d3206c861a2f54e3c1b561 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Wed, 7 Aug 2024 16:21:25 +0200 Subject: [PATCH 7/9] added split map --- examples/RasterLayer.ipynb | 198 +++++++----------------- ipyopenlayers/__init__.py | 2 +- ipyopenlayers/{Map.py => openlayers.py} | 3 +- src/splitcontrol.ts | 118 +++++--------- src/splitmapcontrol.ts | 57 +++---- src/widget.ts | 16 +- 6 files changed, 127 insertions(+), 267 deletions(-) rename ipyopenlayers/{Map.py => openlayers.py} (98%) diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index f4822ec..6ac5f03 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -31,12 +31,14 @@ { "cell_type": "code", "execution_count": 3, - "metadata": {}, + "metadata": { + "scrolled": true + }, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "4c4c19b373ab45dcb40b6f11831de316", + "model_id": "0711a71bef6046faa26be5e90a5eb534", "version_major": 2, "version_minor": 0 }, @@ -90,16 +92,16 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [ - "split = SplitMapControl(left_layer=layere, right_layer=layer_b)" + "split = SplitMapControl(left_layer=layer_b)" ] }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -108,7 +110,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 9, "metadata": {}, "outputs": [], "source": [ @@ -117,198 +119,108 @@ }, { "cell_type": "code", - "execution_count": 8, - "metadata": {}, - "outputs": [], - "source": [ - "attributions = '© MapTiler ' +'© OpenStreetMap contributors';\n", - "\n", - "raster = RasterTileLayer(attributions=attributions,url='https://api.maptiler.com/maps/dataviz-dark/{z}/{x}/{y}.png?key=' + key,\n", - " tileSize= 512)\n", - "m.add_layer(raster) " - ] - }, - { - "cell_type": "code", - "execution_count": 7, + "execution_count": 10, "metadata": {}, "outputs": [ { "data": { - "application/vnd.jupyter.widget-view+json": { - "model_id": "80cf95e1693944cbb9ec94cf2ee3d66f", - "version_major": 2, - "version_minor": 0 - }, "text/plain": [ - "IntSlider(value=0, description='Swipe Position:')" + "[]" ] }, + "execution_count": 10, "metadata": {}, - "output_type": "display_data" - }, + "output_type": "execute_result" + } + ], + "source": [ + "m.controls" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "deea77e51560434c83c56da7ca11ad68", + "model_id": "9ca94f4d55c341d88bec99d839f1d744", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Map(center=[4.299875503991089, 46.85012303279379], layers=[RasterTileLayer(), RasterTileLayer(url='https://api…" + "Map(center=[47.167155938883134, 33.72586333359965], layers=[RasterTileLayer()])" ] }, + "execution_count": 10, "metadata": {}, - "output_type": "display_data" + "output_type": "execute_result" } ], "source": [ - "import ipywidgets as widgets\n", - "# Création du slider\n", - "slider = widgets.IntSlider(\n", - " value=0,\n", - " min=0,\n", - " max=100,\n", - " step=1,\n", - " description='Swipe Position:',\n", - " continuous_update=True\n", - ")\n", - "\n", - "# Créez une fonction pour mettre à jour le modèle lorsque le slider change\n", - "def on_slider_change(change):\n", - " m.swipe_position=change['new']\n", - "\n", - "# Lier la fonction de mise à jour au changement de valeur du slider\n", - "slider.observe(on_slider_change, names='value')\n", - "\n", - "# Affichez le slider dans le notebook\n", - "display(slider)\n", - "display(m)\n" + "m" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "f80524698f8141e4a398dec0b0db53aa", + "model_id": "", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Map(center=[0, 0], layers=[RasterTileLayer(), RasterTileLayer(url='https://api.maptiler.com/maps/dataviz-dark/…" + "Map(center=[0.00033276346057838606, 0.011182868644951327], controls=[SplitMapControl(left_layer=RasterTileLaye…" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ - "# Importer les modules nécessaires\n", - "import ipywidgets as widgets\n", - "from IPython.display import display\n", - "\n", - "map_widget = Map(center=[0, 0], zoom=2)\n", - "left_layer = RasterTileLayer(url='https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png')\n", - "right_layer = RasterTileLayer(url='https://api.maptiler.com/maps/dataviz-dark/{z}/{x}/{y}.png?key=' + key)\n", - "\n", - "# Ajouter les couches au widget de la carte\n", - "map_widget.add_layer(left_layer)\n", - "map_widget.add_layer(right_layer)\n", - "map_widget\n" - ] - }, - { - "cell_type": "code", - "execution_count": 10, - "metadata": { - "collapsed": true, - "jupyter": { - "outputs_hidden": true - } - }, - "outputs": [ - { - "ename": "TraitError", - "evalue": "The 'layers' trait of a Map instance contains an Instance of a List which expected a Layer, not the SplitMapControl SplitMapControl(left_layer=None, right_layer=None).", - "output_type": "error", - "traceback": [ - "\u001b[0;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[0;31mTraitError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[0;32mIn[10], line 3\u001b[0m\n\u001b[1;32m 1\u001b[0m m \u001b[38;5;241m=\u001b[39m Map(center\u001b[38;5;241m=\u001b[39m[\u001b[38;5;241m0\u001b[39m,\u001b[38;5;241m0\u001b[39m], zoom\u001b[38;5;241m=\u001b[39m\u001b[38;5;241m7\u001b[39m)\n\u001b[1;32m 2\u001b[0m control \u001b[38;5;241m=\u001b[39m SplitMapControl()\n\u001b[0;32m----> 3\u001b[0m \u001b[43mm\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43madd_layer\u001b[49m\u001b[43m(\u001b[49m\u001b[43mcontrol\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/Ipy-openlayers/ipyopenlayers/Map.py:147\u001b[0m, in \u001b[0;36mMap.add_layer\u001b[0;34m(self, layer)\u001b[0m\n\u001b[1;32m 146\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21madd_layer\u001b[39m(\u001b[38;5;28mself\u001b[39m, layer):\n\u001b[0;32m--> 147\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mlayers\u001b[49m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlayers \u001b[38;5;241m+\u001b[39m [layer]\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:716\u001b[0m, in \u001b[0;36mTraitType.__set__\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 714\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mread_only:\n\u001b[1;32m 715\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TraitError(\u001b[38;5;124m'\u001b[39m\u001b[38;5;124mThe \u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;132;01m%s\u001b[39;00m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m trait is read-only.\u001b[39m\u001b[38;5;124m'\u001b[39m \u001b[38;5;241m%\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname)\n\u001b[0;32m--> 716\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3635\u001b[0m, in \u001b[0;36mList.set\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3633\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28msuper\u001b[39m()\u001b[38;5;241m.\u001b[39mset(obj, [value]) \u001b[38;5;66;03m# type:ignore[list-item]\u001b[39;00m\n\u001b[1;32m 3634\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 3635\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mset\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:690\u001b[0m, in \u001b[0;36mTraitType.set\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 689\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21mset\u001b[39m(\u001b[38;5;28mself\u001b[39m, obj: HasTraits, value: S) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m--> 690\u001b[0m new_value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 691\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[1;32m 692\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:722\u001b[0m, in \u001b[0;36mTraitType._validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[1;32m 721\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalidate\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 722\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_cross_validation_lock \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m 724\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_cross_validate(obj, value)\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3482\u001b[0m, in \u001b[0;36mContainer.validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3479\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m value \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 3480\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[0;32m-> 3482\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3484\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m t\u001b[38;5;241m.\u001b[39mcast(T, value)\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3629\u001b[0m, in \u001b[0;36mList.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3626\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m length \u001b[38;5;241m<\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_minlen \u001b[38;5;129;01mor\u001b[39;00m length \u001b[38;5;241m>\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_maxlen:\n\u001b[1;32m 3627\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mlength_error(obj, value)\n\u001b[0;32m-> 3629\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m \u001b[38;5;28;43msuper\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43m)\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate_elements\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3494\u001b[0m, in \u001b[0;36mContainer.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3492\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_trait\u001b[38;5;241m.\u001b[39m_validate(obj, v)\n\u001b[1;32m 3493\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TraitError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[0;32m-> 3494\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43merror\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3495\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 3496\u001b[0m validated\u001b[38;5;241m.\u001b[39mappend(v)\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:810\u001b[0m, in \u001b[0;36mTraitType.error\u001b[0;34m(self, obj, value, error, info)\u001b[0m\n\u001b[1;32m 801\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[1;32m 802\u001b[0m error\u001b[38;5;241m.\u001b[39margs \u001b[38;5;241m=\u001b[39m (\n\u001b[1;32m 803\u001b[0m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mThe \u001b[39m\u001b[38;5;124m'\u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m'\u001b[39m\u001b[38;5;124m trait contains \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m which \u001b[39m\u001b[38;5;124m\"\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mexpected \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m, not \u001b[39m\u001b[38;5;132;01m{}\u001b[39;00m\u001b[38;5;124m.\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;241m.\u001b[39mformat(\n\u001b[1;32m 804\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname,\n\u001b[0;32m (...)\u001b[0m\n\u001b[1;32m 808\u001b[0m ),\n\u001b[1;32m 809\u001b[0m )\n\u001b[0;32m--> 810\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m error\n\u001b[1;32m 812\u001b[0m \u001b[38;5;66;03m# this trait caused an error\u001b[39;00m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# this is not the root trait\u001b[39;00m\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:3492\u001b[0m, in \u001b[0;36mContainer.validate_elements\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 3490\u001b[0m \u001b[38;5;28;01mfor\u001b[39;00m v \u001b[38;5;129;01min\u001b[39;00m value:\n\u001b[1;32m 3491\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m-> 3492\u001b[0m v \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_trait\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43m_validate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mv\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 3493\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m TraitError \u001b[38;5;28;01mas\u001b[39;00m error:\n\u001b[1;32m 3494\u001b[0m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39merror(obj, v, error)\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:722\u001b[0m, in \u001b[0;36mTraitType._validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 720\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m value\n\u001b[1;32m 721\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mhasattr\u001b[39m(\u001b[38;5;28mself\u001b[39m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mvalidate\u001b[39m\u001b[38;5;124m\"\u001b[39m):\n\u001b[0;32m--> 722\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43mvalidate\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n\u001b[1;32m 723\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj\u001b[38;5;241m.\u001b[39m_cross_validation_lock \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m:\n\u001b[1;32m 724\u001b[0m value \u001b[38;5;241m=\u001b[39m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39m_cross_validate(obj, value)\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:2311\u001b[0m, in \u001b[0;36mInstance.validate\u001b[0;34m(self, obj, value)\u001b[0m\n\u001b[1;32m 2309\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m t\u001b[38;5;241m.\u001b[39mcast(T, value)\n\u001b[1;32m 2310\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m-> 2311\u001b[0m \u001b[38;5;28;43mself\u001b[39;49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43merror\u001b[49m\u001b[43m(\u001b[49m\u001b[43mobj\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mvalue\u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[0;32m~/.conda/envs/Ipyopenlayer-dev/lib/python3.10/site-packages/traitlets/traitlets.py:815\u001b[0m, in \u001b[0;36mTraitType.error\u001b[0;34m(self, obj, value, error, info)\u001b[0m\n\u001b[1;32m 812\u001b[0m \u001b[38;5;66;03m# this trait caused an error\u001b[39;00m\n\u001b[1;32m 813\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39mname \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[1;32m 814\u001b[0m \u001b[38;5;66;03m# this is not the root trait\u001b[39;00m\n\u001b[0;32m--> 815\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m TraitError(value, info \u001b[38;5;129;01mor\u001b[39;00m \u001b[38;5;28mself\u001b[39m\u001b[38;5;241m.\u001b[39minfo(), \u001b[38;5;28mself\u001b[39m)\n\u001b[1;32m 817\u001b[0m \u001b[38;5;66;03m# this is the root trait\u001b[39;00m\n\u001b[1;32m 818\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m obj \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m:\n", - "\u001b[0;31mTraitError\u001b[0m: The 'layers' trait of a Map instance contains an Instance of a List which expected a Layer, not the SplitMapControl SplitMapControl(left_layer=None, right_layer=None)." - ] - } - ], - "source": [ - "m = Map(center=[0,0], zoom=7)\n", - "control = SplitMapControl()\n", - "m.add_layer(control)" - ] - }, - { - "cell_type": "code", - "execution_count": 8, - "metadata": { - "scrolled": true - }, - "outputs": [], - "source": [ - "m.remove_layer(raster)" + "m" ] }, { "cell_type": "code", - "execution_count": 9, - "metadata": { - "jupyter": { - "source_hidden": true - } - }, + "execution_count": 17, + "metadata": {}, "outputs": [], "source": [ - "attributions = [\n", - " '© MapTiler',\n", - " '© OpenStreetMap contributors'\n", - "]\n", - "\n", - "rasterlay = RasterTileLayer(url=f'https://api.maptiler.com/maps/satellite/{{z}}/{{x}}/{{y}}.jpg?key={key}',attributions=attributions,tileSize=512,maxZoom=20)\n", - "m.add_layer(rasterlay)" + "m.remove_control(split)" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 18, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "249f37982ae74162851c0f1b99a36b1a", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[0.00033276346057838606, 0.011182868644951327], layers=[RasterTileLayer()], zoom=1.9997517395318722…" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "m.remove_layer(rasterlay)" + "m" ] }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] - }, { "cell_type": "code", "execution_count": null, diff --git a/ipyopenlayers/__init__.py b/ipyopenlayers/__init__.py index 4cf0919..0671fce 100644 --- a/ipyopenlayers/__init__.py +++ b/ipyopenlayers/__init__.py @@ -4,7 +4,7 @@ # Copyright (c) QuantStack. # Distributed under the terms of the Modified BSD License. -from .Map import * +from .openlayers import * from ._version import __version__, version_info def _jupyter_labextension_paths(): diff --git a/ipyopenlayers/Map.py b/ipyopenlayers/openlayers.py similarity index 98% rename from ipyopenlayers/Map.py rename to ipyopenlayers/openlayers.py index 79733f8..d4f779b 100644 --- a/ipyopenlayers/Map.py +++ b/ipyopenlayers/openlayers.py @@ -84,7 +84,6 @@ class ImageOverlay (BaseOverlay): class VideoOverlay (BaseOverlay): _view_name = Unicode('VideoOverlayView').tag(sync=True) _model_name = Unicode('VideoOverlayModel').tag(sync=True) - video_url = Unicode('').tag(sync=True) class PopupOverlay (BaseOverlay): @@ -122,7 +121,7 @@ class SplitMapControl(BaseControl): _model_name = Unicode('SplitMapControlModel').tag(sync=True) _view_name = Unicode('SplitMapControlView').tag(sync=True) left_layer = Instance(Layer).tag(sync=True, **widget_serialization) - right_layer = Instance(Layer).tag(sync=True, **widget_serialization) + #right_layer = Instance(Layer).tag(sync=True, **widget_serialization) swipe_position = Int(50).tag(sync=True) diff --git a/src/splitcontrol.ts b/src/splitcontrol.ts index 816e5a5..8f15ef5 100644 --- a/src/splitcontrol.ts +++ b/src/splitcontrol.ts @@ -1,5 +1,3 @@ -// Copyright (c) QuantStack -// Distributed under the terms of the Modified BSD License. import Control from 'ol/control/Control'; import { getRenderPixel } from 'ol/render'; import 'ol/ol.css'; @@ -10,19 +8,16 @@ import { MapView } from './widget'; interface SplitMapControlOptions { target?: string; map_view?: MapView; - swipe_position?: number; // Ajoutez swipe_position en tant que nombre + swipe_position?: number; } + export default class SplitMapControl extends Control { swipe: HTMLInputElement; leftLayer: any; - rightLayer: any; map_view: MapView; - swipe_position: any; - constructor( - leftLayer: any, - rightLayer: any, - options: SplitMapControlOptions = {}, - ) { + private _swipe_position: number; + + constructor(leftLayer: any, options: SplitMapControlOptions = {}) { const element = document.createElement('div'); element.className = 'ol-unselectable ol-control split-map-control'; @@ -32,98 +27,63 @@ export default class SplitMapControl extends Control { }); this.leftLayer = leftLayer; - this.rightLayer = rightLayer; - console.log('Initializing SplitMapControl...'); - console.log(options); if (options.map_view) { this.map_view = options.map_view; - console.log('MapView initialized:', this.map_view); } else { throw new Error('MapView is required for SplitMapControl.'); } - if (options.swipe_position) { - this.swipe_position = options.swipe_position; - console.log('swipe_position initialized:', this.swipe_position); - } else { - throw new Error('swipe_position is required for SplitMapControl.'); - } + + this._swipe_position = options.swipe_position ?? 0; const swiperContainer = document.createElement('div'); swiperContainer.className = 'swiper-container'; + swiperContainer.style.width = '100%'; this.swipe = document.createElement('input'); this.swipe.type = 'range'; this.swipe.className = 'swipe'; - (this.swipe.value = this.swipe_position), - swiperContainer.appendChild(this.swipe); - console.log('this.map_view.map_container', this.map_view.map_container); - this.map_view.map_container.appendChild(swiperContainer); - this.handleSwipe(); - } - - handleSwipe(event?: Event) { - console.log('Handling swipe event...'); - console.log(this.swipe.value); - if (!this.leftLayer.hasListener('prerender')) { - console.log('1'); - this.leftLayer.on('prerender', this.prerender.bind(this)); - } - - if (!this.leftLayer.hasListener('postrender')) { - console.log('2'); - //this.leftLayer.on('postrender', this.postrender.bind(this)); - } - - if (!this.rightLayer.hasListener('prerender')) { - console.log('3'); + this.swipe.style.width = '100%'; + this.updateSwipeValue(); + swiperContainer.appendChild(this.swipe); - //this.rightLayer.on('prerender', this.prerender.bind(this)); - } - - if (!this.rightLayer.hasListener('postrender')) { - console.log('4'); - - this.rightLayer.on('postrender', this.postrender.bind(this)); - } + this.map_view.map_container.style.position = 'relative'; + this.map_view.map_container.appendChild(swiperContainer); - console.log('ok'); - } + const map_view = this.map_view; - prerender(event: any) { - console.log('Prerender event triggered.'); + this.leftLayer.on('prerender', (event: any) => { + const gl = event.context; + gl.enable(gl.SCISSOR_TEST); - const gl = event.context; // Get WebGL context - gl.enable(gl.SCISSOR_TEST); // Enable scissor test + const mapSize = map_view.getSize(); - const mapSize = this.map_view.getSize(); // Get the size of the map - if (!mapSize) { - console.warn('Map size is undefined.'); - return; - } + if (mapSize) { + const bottomLeft = getRenderPixel(event, [0, mapSize[1]]); + const topRight = getRenderPixel(event, [mapSize[0], 0]); - // Get render pixels for the bottom left and top right corners - const bottomLeft = getRenderPixel(event, [0, mapSize[1]]); - const topRight = getRenderPixel(event, [mapSize[0], 0]); + const width = Math.round((topRight[0] - bottomLeft[0]) * (this._swipe_position / 100)); + const height = topRight[1] - bottomLeft[1]; - // Get the swipe value from the input element - const swipeValue = this.swipe.value; - console.log('swipeValue', swipeValue); + gl.scissor(bottomLeft[0], bottomLeft[1], width, height); + } + }); - // Calculate the width and height for the scissor test - const width = Math.round( - (topRight[0] - bottomLeft[0]) * (Number(swipeValue) / 100), - ); - const height = topRight[1] - bottomLeft[1]; + this.leftLayer.on('postrender', (event: any) => { + const gl = event.context; + gl.disable(gl.SCISSOR_TEST); + }); - // Define the scissor box - gl.scissor(bottomLeft[0], bottomLeft[1], width, height); + this.swipe.addEventListener('input', () => { + this._swipe_position = parseInt(this.swipe.value, 10); + this.updateSwipeValue(); + map_view.map.render(); + }); } - postrender(event: any) { - console.log('Postrender event triggered.'); - - const gl = event.context; // Get WebGL context - gl.disable(gl.SCISSOR_TEST); // Disable scissor test + private updateSwipeValue() { + if (this.swipe) { + this.swipe.value = this._swipe_position.toString(); + } } } diff --git a/src/splitmapcontrol.ts b/src/splitmapcontrol.ts index 5a48e0f..f3cedfb 100644 --- a/src/splitmapcontrol.ts +++ b/src/splitmapcontrol.ts @@ -1,30 +1,41 @@ // Copyright (c) QuantStack // Distributed under the terms of the Modified BSD License. -import { unpack_models } from '@jupyter-widgets/base'; +import { unpack_models, ISerializers } from '@jupyter-widgets/base'; import { BaseControlModel, BaseControlView } from './basecontrol'; import 'ol/ol.css'; import '../css/widget.css'; import SplitMapControl from './splitcontrol'; +import { MODULE_NAME, MODULE_VERSION } from './version'; -// Modèle pour le contrôle de carte divisée export class SplitMapControlModel extends BaseControlModel { defaults() { return { ...super.defaults(), - _view_name: 'SplitMapControlView', - _model_name: 'SplitMapControlModel', + _model_name: SplitMapControlModel.model_name, + _model_module: SplitMapControlModel.model_module, + _model_module_version: SplitMapControlModel.model_module_version, + _view_name: SplitMapControlModel.view_name, + _view_module: SplitMapControlModel.view_module, + _view_module_version: SplitMapControlModel.view_module_version, left_layer: undefined, right_layer: undefined, swipe_position: 50, }; } -} -SplitMapControlModel.serializers = { - ...BaseControlModel.serializers, - left_layer: { deserialize: unpack_models }, - right_layer: { deserialize: unpack_models }, -}; + static serializers: ISerializers = { + ...BaseControlModel.serializers, + left_layer: { deserialize: unpack_models }, + right_layer: { deserialize: unpack_models }, + }; + + static model_name = 'SplitMapControlModel'; + static model_module = MODULE_NAME; + static model_module_version = MODULE_VERSION; + static view_name = 'SplitMapControlView'; + static view_module = MODULE_NAME; + static view_module_version = MODULE_VERSION; +} function asArray(arg: any) { return Array.isArray(arg) ? arg : [arg]; @@ -32,53 +43,35 @@ function asArray(arg: any) { export class SplitMapControlView extends BaseControlView { swipe_position: number; + initialize(parameters: any) { super.initialize(parameters); this.map_view = this.options.map_view; this.map_view.layer_views = this.options.map_view.layerViews; - console.log('hello'); if (this.map_view && !this.map_view.layerViews) { - console.warn( - 'Layer views is not initialized. Ensure it is properly set.', - ); + console.warn('Layer views is not initialized. Ensure it is properly set.'); } } createObj() { - console.log('Creating SplitMapControl object...'); const left_models = asArray(this.model.get('left_layer')); - - const right_models = asArray(this.model.get('right_layer')); - let layersModel = this.map_view.model.get('layers'); - - layersModel = layersModel.concat(left_models, right_models); + layersModel = layersModel.concat(left_models); return this.map_view.layer_views.update(layersModel).then((views: any) => { const left_views: any[] = []; - console.log(left_views); - const right_views: any[] = []; - console.log(right_views); - views.forEach((view: any) => { if (left_models.indexOf(view.model) !== -1) { left_views.push(view.obj); } - if (right_models.indexOf(view.model) !== -1) { - right_views.push(view.obj); - } }); - console.log('Left views:', left_views); - console.log('Right views:', right_views); this.swipe_position = this.model.get('swipe_position'); - console.log('swipe_position:', this.swipe_position); - this.obj = new SplitMapControl(left_views[0], right_views[0], { + this.obj = new SplitMapControl(left_views[0], { map_view: this.map_view, swipe_position: this.swipe_position, }); - console.log('SplitMapControl created:', this.obj); }); } } diff --git a/src/widget.ts b/src/widget.ts index baeb9fc..f167e74 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -11,10 +11,8 @@ import { LayerModel, LayerView } from './layer'; import { BaseOverlayModel, BaseOverlayView } from './baseoverlay'; import { BaseControlModel, BaseControlView } from './basecontrol'; import { ViewObjectEventTypes } from 'ol/View'; -// widget.ts import { SplitMapControlModel, SplitMapControlView } from './splitmapcontrol'; export { SplitMapControlModel, SplitMapControlView }; - import { Map } from 'ol'; import View from 'ol/View'; import 'ol/ol.css'; @@ -35,10 +33,9 @@ export * from './rastertilelayer'; export * from './geotifflayer'; export * from './vectortilelayer'; export * from './splitmapcontrol'; +export * from './splitcontrol'; + -/*type WebGLEvent = { - context: WebGLRenderingContext; -};*/ const DEFAULT_LOCATION = [0.0, 0.0]; export class MapModel extends DOMWidgetModel { @@ -135,10 +132,11 @@ export class MapView extends DOMWidgetView { this.model.set('zoom', this.map.getView().getZoom()); this.model.save_changes(); }); - this.layersChanged(); - this.overlayChanged(); this.controlChanged(); + this.overlayChanged(); + this.zoomChanged(); + this.centerChanged(); this.model.on('change:layers', this.layersChanged, this); this.model.on('change:overlays', this.overlayChanged, this); this.model.on('change:controls', this.controlChanged, this); @@ -194,17 +192,15 @@ export class MapView extends DOMWidgetView { this.map.removeControl(child_view.obj); child_view.remove(); } - + async addLayerModel(child_model: LayerModel) { const view = await this.create_child_view(child_model, { map_view: this, }); this.map.addLayer(view.obj); - console.log(this.map); this.displayed.then(() => { view.trigger('displayed', this); }); - console.log(view); return view; } From 9c643ca57f03059ecb3e1f70c8015a702ac1f534 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Wed, 7 Aug 2024 17:19:08 +0200 Subject: [PATCH 8/9] added remove funtion --- examples/RasterLayer.ipynb | 69 ++++++++++++++++++++++++++++++++----- ipyopenlayers/openlayers.py | 25 +++++++++++++- src/splitcontrol.ts | 4 ++- src/splitmapcontrol.ts | 4 ++- src/widget.ts | 3 +- 5 files changed, 91 insertions(+), 14 deletions(-) diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index 6ac5f03..cb948f9 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -13,7 +13,7 @@ "metadata": {}, "outputs": [], "source": [ - "from ipyopenlayers import Map, RasterTileLayer,SplitMapControl" + "from ipyopenlayers import Map, RasterTileLayer,SplitMapControl, ZoomSlider" ] }, { @@ -38,7 +38,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "0711a71bef6046faa26be5e90a5eb534", + "model_id": "118567532fa24b9bbfe23392434a10ff", "version_major": 2, "version_minor": 0 }, @@ -92,7 +92,42 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, + "metadata": {}, + "outputs": [], + "source": [ + "zoom=ZoomSlider()\n", + "m.add_control(zoom)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "118567532fa24b9bbfe23392434a10ff", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[0.0, 0.0], layers=[RasterTileLayer()], zoom=3.1446582428318823)" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "m.remove(zoom)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, "metadata": {}, "outputs": [], "source": [ @@ -101,7 +136,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -110,16 +145,32 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 14, "metadata": {}, - "outputs": [], + "outputs": [ + { + "data": { + "application/vnd.jupyter.widget-view+json": { + "model_id": "118567532fa24b9bbfe23392434a10ff", + "version_major": 2, + "version_minor": 0 + }, + "text/plain": [ + "Map(center=[0.0, 0.0], layers=[RasterTileLayer()], zoom=3.1446582428318823)" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], "source": [ - "m.remove_control(split)" + "m.remove(split)" ] }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, "metadata": {}, "outputs": [ { @@ -128,7 +179,7 @@ "[]" ] }, - "execution_count": 10, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } diff --git a/ipyopenlayers/openlayers.py b/ipyopenlayers/openlayers.py index d4f779b..b3f48f7 100644 --- a/ipyopenlayers/openlayers.py +++ b/ipyopenlayers/openlayers.py @@ -166,8 +166,31 @@ def add_control(self, control): def remove_control(self, control): self.controls = [x for x in self.controls if x != control] - + + def remove(self, item): + """Remove an item from the map : either a layer or a control. + + Parameters + ---------- + item: Layer or Control instance + The layer or control to remove. + """ + if isinstance(item, Layer): + self.layers = tuple( + [layer for layer in self.layers if layer.model_id != item.model_id] + ) + + elif isinstance(item, BaseControl): + self.controls = tuple( + [ + control + for control in self.controls + if control.model_id != item.model_id + ] + ) + return self + def clear_layers(self): self.layers = [] diff --git a/src/splitcontrol.ts b/src/splitcontrol.ts index 8f15ef5..9868ac1 100644 --- a/src/splitcontrol.ts +++ b/src/splitcontrol.ts @@ -62,7 +62,9 @@ export default class SplitMapControl extends Control { const bottomLeft = getRenderPixel(event, [0, mapSize[1]]); const topRight = getRenderPixel(event, [mapSize[0], 0]); - const width = Math.round((topRight[0] - bottomLeft[0]) * (this._swipe_position / 100)); + const width = Math.round( + (topRight[0] - bottomLeft[0]) * (this._swipe_position / 100), + ); const height = topRight[1] - bottomLeft[1]; gl.scissor(bottomLeft[0], bottomLeft[1], width, height); diff --git a/src/splitmapcontrol.ts b/src/splitmapcontrol.ts index f3cedfb..511bbaf 100644 --- a/src/splitmapcontrol.ts +++ b/src/splitmapcontrol.ts @@ -49,7 +49,9 @@ export class SplitMapControlView extends BaseControlView { this.map_view = this.options.map_view; this.map_view.layer_views = this.options.map_view.layerViews; if (this.map_view && !this.map_view.layerViews) { - console.warn('Layer views is not initialized. Ensure it is properly set.'); + console.warn( + 'Layer views is not initialized. Ensure it is properly set.', + ); } } diff --git a/src/widget.ts b/src/widget.ts index f167e74..9eaa924 100644 --- a/src/widget.ts +++ b/src/widget.ts @@ -35,7 +35,6 @@ export * from './vectortilelayer'; export * from './splitmapcontrol'; export * from './splitcontrol'; - const DEFAULT_LOCATION = [0.0, 0.0]; export class MapModel extends DOMWidgetModel { @@ -192,7 +191,7 @@ export class MapView extends DOMWidgetView { this.map.removeControl(child_view.obj); child_view.remove(); } - + async addLayerModel(child_model: LayerModel) { const view = await this.create_child_view(child_model, { map_view: this, From 753d79e9a7e26b85d6ecacf5366ba576f1bb7000 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Wed, 14 Aug 2024 16:23:05 +0200 Subject: [PATCH 9/9] model problem --- examples/RasterLayer.ipynb | 32 ++++++++++---------------------- 1 file changed, 10 insertions(+), 22 deletions(-) diff --git a/examples/RasterLayer.ipynb b/examples/RasterLayer.ipynb index cb948f9..f5958bd 100644 --- a/examples/RasterLayer.ipynb +++ b/examples/RasterLayer.ipynb @@ -19,18 +19,6 @@ { "cell_type": "code", "execution_count": 2, - "metadata": {}, - "outputs": [], - "source": [ - "import configparser\n", - "config = configparser.ConfigParser()\n", - "config.read('.config.ini')\n", - "key = config['DEFAULT']['key']" - ] - }, - { - "cell_type": "code", - "execution_count": 3, "metadata": { "scrolled": true }, @@ -38,7 +26,7 @@ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "118567532fa24b9bbfe23392434a10ff", + "model_id": "eb8f7885d1b94654942794f633a79e87", "version_major": 2, "version_minor": 0 }, @@ -46,7 +34,7 @@ "Map(center=[0.0, 0.0])" ] }, - "execution_count": 3, + "execution_count": 2, "metadata": {}, "output_type": "execute_result" } @@ -74,7 +62,7 @@ }, { "cell_type": "code", - "execution_count": 5, + "execution_count": 3, "metadata": {}, "outputs": [], "source": [ @@ -92,7 +80,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 4, "metadata": {}, "outputs": [], "source": [ @@ -102,21 +90,21 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 5, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { - "model_id": "118567532fa24b9bbfe23392434a10ff", + "model_id": "eb8f7885d1b94654942794f633a79e87", "version_major": 2, "version_minor": 0 }, "text/plain": [ - "Map(center=[0.0, 0.0], layers=[RasterTileLayer()], zoom=3.1446582428318823)" + "Map(center=[0.0, 0.0])" ] }, - "execution_count": 10, + "execution_count": 5, "metadata": {}, "output_type": "execute_result" } @@ -127,7 +115,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 6, "metadata": {}, "outputs": [], "source": [ @@ -136,7 +124,7 @@ }, { "cell_type": "code", - "execution_count": 12, + "execution_count": 7, "metadata": {}, "outputs": [], "source": [