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;