From 831b9042a8c1ae94a7a339b58a58a2a65a8cc4b3 Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Wed, 31 Jul 2024 10:19:57 +0200 Subject: [PATCH 1/2] 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 55ad7b9ab237a961c12090a9a2bab8b0c8d7a65f Mon Sep 17 00:00:00 2001 From: Nour-Cheour10 Date: Fri, 2 Aug 2024 10:08:38 +0200 Subject: [PATCH 2/2] 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);