Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ImageSource.updateImage() #7342

Merged
merged 11 commits into from
Oct 2, 2018
69 changes: 69 additions & 0 deletions debug/update_image.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
<!DOCTYPE html>
<html>
<head>
<title>Mapbox GL JS debug page</title>
<meta charset='utf-8'>
<meta name="viewport" content="width=device-width, initial-scale=1.0, user-scalable=no">
<link rel='stylesheet' href='/dist/mapbox-gl.css' />
<style>
body { margin: 0; padding: 0; }
html, body, #map { height: 100%; }
</style>
</head>

<body>
<div id='map'></div>

<script src='/dist/mapbox-gl-dev.js'></script>
<script src='/debug/access_token_generated.js'></script>
<script>

const map = new mapboxgl.Map({
container: 'map',
minZoom: 1,
zoom: 6,
center: [-76, 43.5],
style: 'mapbox://styles/mapbox/dark-v9',
hash: false
});

const path = "/docs/pages/assets/";

const frameCount = 5;

const coordinates = [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
];

map.on("load", () => {
let currentImage = 0;
const getPath = () => `${path}radar${currentImage}.gif`;

map.addSource("radar", {
type: "image",
url: getPath(),
coordinates
});
map.addLayer({
id: "radar-layer",
"type": "raster",
"source": "radar",
"paint": {
"raster-resampling": "nearest",
"raster-fade-duration": 0
}
});

setInterval(() => {
currentImage = (currentImage + 1) % frameCount;
map.getSource("radar").updateImage({ url: getPath() });
}, 200);
});

</script>

</body>
</html>
56 changes: 27 additions & 29 deletions docs/pages/example/animate-images.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,39 +9,37 @@
center: [-75.789, 41.874]
});

map.on('load', function() {
var frameCount = 5;
var currentImage = 0;

function getPath() {
return "https://www.mapbox.com/mapbox-gl-js/assets/radar" + currentImage + ".gif";
}

var frameCount = 5;
for (var i = 0; i < frameCount; i++) {
map.on('load', function() {

map.addLayer({
id: 'radar' + i,
source: {
type: 'image',
url: 'https://www.mapbox.com/mapbox-gl-js/assets/radar' + i + '.gif',
coordinates: [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
]
},
type: 'raster',
paint: {
'raster-opacity': 0,
'raster-opacity-transition': {
duration: 0
}
}
});
}
map.addSource("radar", {
type: "image",
url: getPath(),
coordinates: [
[-80.425, 46.437],
[-71.516, 46.437],
[-71.516, 37.936],
[-80.425, 37.936]
]
});
map.addLayer({
id: "radar-layer",
"type": "raster",
"source": "radar",
"paint": {
"raster-fade-duration": 0
}
});

var frame = frameCount - 1;
setInterval(function() {
map.setPaintProperty('radar' + frame, 'raster-opacity', 0);
frame = (frame + 1) % frameCount;
map.setPaintProperty('radar' + frame, 'raster-opacity', 1);
currentImage = (currentImage + 1) % frameCount;
map.getSource("radar").updateImage({ url: getPath() });
}, 200);

});
</script>
52 changes: 46 additions & 6 deletions src/source/image_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import type {
VideoSourceSpecification
} from '../style-spec/types';

type Coordinates = [[number, number], [number, number], [number, number], [number, number]];

/**
* A data source containing an image.
* (See the [Style Specification](https://www.mapbox.com/mapbox-gl-style-spec/#sources-image) for detailed documentation of options.)
Expand All @@ -44,15 +46,26 @@ import type {
* ]
* });
*
* // update
* // update coordinates
* var mySource = map.getSource('some id');
* mySource.setCoordinates([
* [-76.54335737228394, 39.18579907229748],
* [-76.52803659439087, 39.1838364847587],
* [-76.5295386314392, 39.17683392507606],
* [-76.54520273208618, 39.17876344106642]
* ]);
*
*
* // update url and coordinates simultaneously
* mySource.updateImage({
* url: 'https://www.mapbox.com/images/bar.png',
* coordinates: [
* [-76.54335737228394, 39.18579907229748],
* [-76.52803659439087, 39.1838364847587],
* [-76.5295386314392, 39.17683392507606],
* [-76.54520273208618, 39.17876344106642]
* ]
* })
*
* map.removeSource('some id'); // remove
* @see [Add an image](https://www.mapbox.com/mapbox-gl-js/example/image-on-a-map/)
*/
Expand All @@ -64,12 +77,12 @@ class ImageSource extends Evented implements Source {
tileSize: number;
url: string;

coordinates: [[number, number], [number, number], [number, number], [number, number]];
coordinates: Coordinates;
tiles: {[string]: Tile};
options: any;
dispatcher: Dispatcher;
map: Map;
texture: Texture;
texture: Texture | null;
image: ImageData;
centerCoord: Coordinate;
tileID: CanonicalTileID;
Expand Down Expand Up @@ -97,7 +110,7 @@ class ImageSource extends Evented implements Source {
this.options = options;
}

load() {
load(newCoordinates?: Coordinates, successCallback?: () => void) {
this.fire(new Event('dataloading', {dataType: 'source'}));

this.url = this.options.url;
Expand All @@ -107,11 +120,38 @@ class ImageSource extends Evented implements Source {
this.fire(new ErrorEvent(err));
} else if (image) {
this.image = browser.getImageData(image);
if (newCoordinates) {
this.coordinates = newCoordinates;
}
if (successCallback) {
successCallback();
}
this._finishLoading();
}
});
}

/**
* Updates the image URL and, optionally, the coordinates. To avoid having the image flash after changing,
* set the `raster-fade-duration` paint property on the raster layer to 0.
*
* @param {Object} options
* @param {string} [options.url] Required image URL.
* @param {Array<Array<number>>} [options.coordinates] Four geographical coordinates,
* represented as arrays of longitude and latitude numbers, which define the corners of the image.
* The coordinates start at the top left corner of the image and proceed in clockwise order.
* They do not have to represent a rectangle.
* @returns {ImageSource} this
*/
updateImage(options: {url: string, coordinates?: Coordinates}) {
if (!this.image || !options.url) {
return this;
}
this.options.url = options.url;
this.load(options.coordinates, () => { this.texture = null; });
return this;
}

_finishLoading() {
if (this.map) {
this.setCoordinates(this.coordinates);
Expand All @@ -133,7 +173,7 @@ class ImageSource extends Evented implements Source {
* They do not have to represent a rectangle.
* @returns {ImageSource} this
*/
setCoordinates(coordinates: [[number, number], [number, number], [number, number], [number, number]]) {
setCoordinates(coordinates: Coordinates) {
this.coordinates = coordinates;

// Calculate which mercator tile is suitable for rendering the video in
Expand Down
47 changes: 47 additions & 0 deletions test/unit/source/image_source.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,6 +81,53 @@ test('ImageSource', (t) => {
t.end();
});

t.test('updates url from updateImage', (t) => {
const source = createSource({ url : '/image.png' });
const map = new StubMap();
const spy = t.spy(map, '_transformRequest');
source.onAdd(map);
respond();
t.ok(spy.calledOnce);
t.equal(spy.getCall(0).args[0], '/image.png');
t.equal(spy.getCall(0).args[1], 'Image');
source.updateImage({ url: '/image2.png' });
respond();
t.ok(spy.calledTwice);
t.equal(spy.getCall(1).args[0], '/image2.png');
t.equal(spy.getCall(1).args[1], 'Image');
t.end();
});

t.test('sets coordinates', (t) => {
const source = createSource({ url : '/image.png' });
const map = new StubMap();
source.onAdd(map);
respond();
const beforeSerialized = source.serialize();
t.deepEqual(beforeSerialized.coordinates, [[0, 0], [1, 0], [1, 1], [0, 1]]);
source.setCoordinates([[0, 0], [-1, 0], [-1, -1], [0, -1]]);
const afterSerialized = source.serialize();
t.deepEqual(afterSerialized.coordinates, [[0, 0], [-1, 0], [-1, -1], [0, -1]]);
t.end();
});

t.test('sets coordinates via updateImage', (t) => {
const source = createSource({ url : '/image.png' });
const map = new StubMap();
source.onAdd(map);
respond();
const beforeSerialized = source.serialize();
t.deepEqual(beforeSerialized.coordinates, [[0, 0], [1, 0], [1, 1], [0, 1]]);
source.updateImage({
url: '/image2.png',
coordinates: [[0, 0], [-1, 0], [-1, -1], [0, -1]]
});
respond();
const afterSerialized = source.serialize();
t.deepEqual(afterSerialized.coordinates, [[0, 0], [-1, 0], [-1, -1], [0, -1]]);
t.end();
});

t.test('fires data event when content is loaded', (t) => {
const source = createSource({ url : '/image.png' });
source.on('data', (e) => {
Expand Down