UNPKG

7.47 kBJavaScriptView Raw
1import {Layer} from './Layer';
2import * as Util from '../core/Util';
3import {toLatLngBounds} from '../geo/LatLngBounds';
4import {Bounds} from '../geometry/Bounds';
5import * as DomUtil from '../dom/DomUtil';
6
7/*
8 * @class ImageOverlay
9 * @aka L.ImageOverlay
10 * @inherits Interactive layer
11 *
12 * Used to load and display a single image over specific bounds of the map. Extends `Layer`.
13 *
14 * @example
15 *
16 * ```js
17 * var imageUrl = 'https://maps.lib.utexas.edu/maps/historical/newark_nj_1922.jpg',
18 * imageBounds = [[40.712216, -74.22655], [40.773941, -74.12544]];
19 * L.imageOverlay(imageUrl, imageBounds).addTo(map);
20 * ```
21 */
22
23export var ImageOverlay = Layer.extend({
24
25 // @section
26 // @aka ImageOverlay options
27 options: {
28 // @option opacity: Number = 1.0
29 // The opacity of the image overlay.
30 opacity: 1,
31
32 // @option alt: String = ''
33 // Text for the `alt` attribute of the image (useful for accessibility).
34 alt: '',
35
36 // @option interactive: Boolean = false
37 // If `true`, the image overlay will emit [mouse events](#interactive-layer) when clicked or hovered.
38 interactive: false,
39
40 // @option crossOrigin: Boolean|String = false
41 // Whether the crossOrigin attribute will be added to the image.
42 // If a String is provided, the image will have its crossOrigin attribute set to the String provided. This is needed if you want to access image pixel data.
43 // Refer to [CORS Settings](https://developer.mozilla.org/en-US/docs/Web/HTML/CORS_settings_attributes) for valid String values.
44 crossOrigin: false,
45
46 // @option errorOverlayUrl: String = ''
47 // URL to the overlay image to show in place of the overlay that failed to load.
48 errorOverlayUrl: '',
49
50 // @option zIndex: Number = 1
51 // The explicit [zIndex](https://developer.mozilla.org/docs/Web/CSS/CSS_Positioning/Understanding_z_index) of the overlay layer.
52 zIndex: 1,
53
54 // @option className: String = ''
55 // A custom class name to assign to the image. Empty by default.
56 className: ''
57 },
58
59 initialize: function (url, bounds, options) { // (String, LatLngBounds, Object)
60 this._url = url;
61 this._bounds = toLatLngBounds(bounds);
62
63 Util.setOptions(this, options);
64 },
65
66 onAdd: function () {
67 if (!this._image) {
68 this._initImage();
69
70 if (this.options.opacity < 1) {
71 this._updateOpacity();
72 }
73 }
74
75 if (this.options.interactive) {
76 DomUtil.addClass(this._image, 'leaflet-interactive');
77 this.addInteractiveTarget(this._image);
78 }
79
80 this.getPane().appendChild(this._image);
81 this._reset();
82 },
83
84 onRemove: function () {
85 DomUtil.remove(this._image);
86 if (this.options.interactive) {
87 this.removeInteractiveTarget(this._image);
88 }
89 },
90
91 // @method setOpacity(opacity: Number): this
92 // Sets the opacity of the overlay.
93 setOpacity: function (opacity) {
94 this.options.opacity = opacity;
95
96 if (this._image) {
97 this._updateOpacity();
98 }
99 return this;
100 },
101
102 setStyle: function (styleOpts) {
103 if (styleOpts.opacity) {
104 this.setOpacity(styleOpts.opacity);
105 }
106 return this;
107 },
108
109 // @method bringToFront(): this
110 // Brings the layer to the top of all overlays.
111 bringToFront: function () {
112 if (this._map) {
113 DomUtil.toFront(this._image);
114 }
115 return this;
116 },
117
118 // @method bringToBack(): this
119 // Brings the layer to the bottom of all overlays.
120 bringToBack: function () {
121 if (this._map) {
122 DomUtil.toBack(this._image);
123 }
124 return this;
125 },
126
127 // @method setUrl(url: String): this
128 // Changes the URL of the image.
129 setUrl: function (url) {
130 this._url = url;
131
132 if (this._image) {
133 this._image.src = url;
134 }
135 return this;
136 },
137
138 // @method setBounds(bounds: LatLngBounds): this
139 // Update the bounds that this ImageOverlay covers
140 setBounds: function (bounds) {
141 this._bounds = toLatLngBounds(bounds);
142
143 if (this._map) {
144 this._reset();
145 }
146 return this;
147 },
148
149 getEvents: function () {
150 var events = {
151 zoom: this._reset,
152 viewreset: this._reset
153 };
154
155 if (this._zoomAnimated) {
156 events.zoomanim = this._animateZoom;
157 }
158
159 return events;
160 },
161
162 // @method setZIndex(value: Number): this
163 // Changes the [zIndex](#imageoverlay-zindex) of the image overlay.
164 setZIndex: function (value) {
165 this.options.zIndex = value;
166 this._updateZIndex();
167 return this;
168 },
169
170 // @method getBounds(): LatLngBounds
171 // Get the bounds that this ImageOverlay covers
172 getBounds: function () {
173 return this._bounds;
174 },
175
176 // @method getElement(): HTMLElement
177 // Returns the instance of [`HTMLImageElement`](https://developer.mozilla.org/docs/Web/API/HTMLImageElement)
178 // used by this overlay.
179 getElement: function () {
180 return this._image;
181 },
182
183 _initImage: function () {
184 var wasElementSupplied = this._url.tagName === 'IMG';
185 var img = this._image = wasElementSupplied ? this._url : DomUtil.create('img');
186
187 DomUtil.addClass(img, 'leaflet-image-layer');
188 if (this._zoomAnimated) { DomUtil.addClass(img, 'leaflet-zoom-animated'); }
189 if (this.options.className) { DomUtil.addClass(img, this.options.className); }
190
191 img.onselectstart = Util.falseFn;
192 img.onmousemove = Util.falseFn;
193
194 // @event load: Event
195 // Fired when the ImageOverlay layer has loaded its image
196 img.onload = Util.bind(this.fire, this, 'load');
197 img.onerror = Util.bind(this._overlayOnError, this, 'error');
198
199 if (this.options.crossOrigin || this.options.crossOrigin === '') {
200 img.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin;
201 }
202
203 if (this.options.zIndex) {
204 this._updateZIndex();
205 }
206
207 if (wasElementSupplied) {
208 this._url = img.src;
209 return;
210 }
211
212 img.src = this._url;
213 img.alt = this.options.alt;
214 },
215
216 _animateZoom: function (e) {
217 var scale = this._map.getZoomScale(e.zoom),
218 offset = this._map._latLngBoundsToNewLayerBounds(this._bounds, e.zoom, e.center).min;
219
220 DomUtil.setTransform(this._image, offset, scale);
221 },
222
223 _reset: function () {
224 var image = this._image,
225 bounds = new Bounds(
226 this._map.latLngToLayerPoint(this._bounds.getNorthWest()),
227 this._map.latLngToLayerPoint(this._bounds.getSouthEast())),
228 size = bounds.getSize();
229
230 DomUtil.setPosition(image, bounds.min);
231
232 image.style.width = size.x + 'px';
233 image.style.height = size.y + 'px';
234 },
235
236 _updateOpacity: function () {
237 DomUtil.setOpacity(this._image, this.options.opacity);
238 },
239
240 _updateZIndex: function () {
241 if (this._image && this.options.zIndex !== undefined && this.options.zIndex !== null) {
242 this._image.style.zIndex = this.options.zIndex;
243 }
244 },
245
246 _overlayOnError: function () {
247 // @event error: Event
248 // Fired when the ImageOverlay layer fails to load its image
249 this.fire('error');
250
251 var errorUrl = this.options.errorOverlayUrl;
252 if (errorUrl && this._url !== errorUrl) {
253 this._url = errorUrl;
254 this._image.src = errorUrl;
255 }
256 },
257
258 // @method getCenter(): LatLng
259 // Returns the center of the ImageOverlay.
260 getCenter: function () {
261 return this._bounds.getCenter();
262 }
263});
264
265// @factory L.imageOverlay(imageUrl: String, bounds: LatLngBounds, options?: ImageOverlay options)
266// Instantiates an image overlay object given the URL of the image and the
267// geographical bounds it is tied to.
268export var imageOverlay = function (url, bounds, options) {
269 return new ImageOverlay(url, bounds, options);
270};