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

Adds support for Mapzen terrain #6024

Closed
wants to merge 18 commits into from
Closed
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
45 changes: 45 additions & 0 deletions debug/hillshade-mapzen.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
<!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>

var map = new mapboxgl.Map({
container: 'map',
style: 'mapbox://styles/mapbox/cjaudgl840gn32rnrepcb9b9g', // the outdoors-v10 style but without Hillshade layers
center: [-119.5591, 37.715],
zoom: 9
});

map.on('load', function () {
map.addSource('dem', {
"type": "raster-dem",
"encoding": "mapzen",
"url": "https://tile.mapzen.com/mapzen/terrain/v1/{format}/{z}/{x}/{y}.png?api_key=mapzen-KgvPGAV"
});
map.addLayer({
"id": "hillshading",
"source": "dem",
"type": "hillshade"
// insert below waterway-river-canal-shadow;
// where hillshading sits in the Mapbox Outdoors style
}, 'waterway-river-canal-shadow');
});

</script>
</body>
</html>
3 changes: 2 additions & 1 deletion flow-typed/style-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,8 @@ declare type RasterDEMSourceSpecification = {
"minzoom"?: number,
"maxzoom"?: number,
"tileSize"?: number,
"attribution"?: string
"attribution"?: string,
"encoding"?: string
}

declare type GeojsonSourceSpecification = {|
Expand Down
46 changes: 36 additions & 10 deletions src/data/dem_data.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,22 +57,14 @@ class DEMData {
this.loaded = !!data;
}

loadFromImage(data: RGBAImage) {
loadFromImage(data: RGBAImage, encoding: string) {
if (data.height !== data.width) throw new RangeError('DEM tiles must be square');

// Build level 0
const level = this.level = new Level(data.width, data.width / 2);
const pixels = data.data;

// unpack
for (let y = 0; y < level.dim; y++) {
for (let x = 0; x < level.dim; x++) {
const i = y * level.dim + x;
const j = i * 4;
// decoding per https://blog.mapbox.com/global-elevation-data-6689f1d0ba65
level.set(x, y, this.scale * ((pixels[j] * 256 * 256 + pixels[j + 1] * 256.0 + pixels[j + 2]) / 10.0 - 10000.0));
}
}
this.unpackData(level, data, encoding);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

needs to be data.data


// in order to avoid flashing seams between tiles, here we are initially populating a 1px border of pixels around the image
// with the data of the nearest pixel from the image. this data is eventually replaced when the tile's neighboring
Expand All @@ -95,6 +87,40 @@ class DEMData {
this.loaded = true;
}

unpackData(level: Level, pixels: Uint8Array, encoding: string) {

switch(encoding) {
case 'mapzen':
this.unpackMapzenData(level, pixels);
break;
default:
this.unpackMapboxData(level, pixels);
}

}

unpackMapboxData(level: Level, pixels: Uint8Array) {
for (let y = 0; y < level.dim; y++) {
for (let x = 0; x < level.dim; x++) {
const i = y * level.dim + x;
const j = i * 4;
// decoding per https://blog.mapbox.com/global-elevation-data-6689f1d0ba65
level.set(x, y, this.scale * ((pixels[j] * 256 * 256 + pixels[j + 1] * 256.0 + pixels[j + 2]) / 10.0 - 10000.0));
}
}
}

unpackMapzenData(level: Level, pixels: Uint8Array) {
for (let y = 0; y < level.dim; y++) {
for (let x = 0; x < level.dim; x++) {
const i = y * level.dim + x;
const j = i * 4;
// decoding per https://mapzen.com/documentation/terrain-tiles/formats/#types-of-terrain-tiles
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

link to AWS docs

level.set(x, y, this.scale * ((pixels[j] * 256 + pixels[j + 1] + pixels[j + 2] / 256) - 32768.0));
}
}
}

getPixels() {
return new RGBAImage({width: this.level.dim + 2 * this.level.border, height: this.level.dim + 2 * this.level.border}, new Uint8Array(this.level.data.buffer));
}
Expand Down
7 changes: 6 additions & 1 deletion src/source/raster_dem_tile_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,14 @@ import type {Callback} from '../types/callback';


class RasterDEMTileSource extends RasterTileSource implements Source {
encoding: string;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

you can explicitly type this as encoding: "mapbox" | "terrarium";


constructor(id: string, options: RasterDEMSourceSpecification, dispatcher: Dispatcher, eventedParent: Evented) {
super(id, options, dispatcher, eventedParent);
this.type = 'raster-dem';
this.maxzoom = 22;
this._options = util.extend({}, options);
this.encoding = "mapbox";
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

options.encoding || "mapbox";

}

serialize() {
Expand All @@ -29,6 +32,7 @@ class RasterDEMTileSource extends RasterTileSource implements Source {
tileSize: this.tileSize,
tiles: this.tiles,
bounds: this.bounds,
encoding: this.encoding
};
}

Expand All @@ -55,7 +59,8 @@ class RasterDEMTileSource extends RasterTileSource implements Source {
uid: tile.uid,
coord: tile.tileID,
source: this.id,
rawImageData: rawImageData
rawImageData: rawImageData,
encoding: this.encoding
};

if (!tile.workerID || tile.state === 'expired') {
Expand Down
2 changes: 1 addition & 1 deletion src/source/raster_dem_tile_worker_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ class RasterDEMTileWorkerSource {

const dem = new DEMData(uid);
this.loading[source][uid] = dem;
dem.loadFromImage(params.rawImageData);
dem.loadFromImage(params.rawImageData, params.encoding);
delete this.loading[source][uid];

this.loaded[source] = this.loaded[source] || {};
Expand Down
3 changes: 2 additions & 1 deletion src/source/worker_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@ export type WorkerTileParameters = TileParameters & {

export type WorkerDEMTileParameters = TileParameters & {
coord: { z: number, x: number, y: number, w: number },
rawImageData: RGBAImage
rawImageData: RGBAImage,
encoding: string
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

explicitly type for enum values (as above)

};

export type WorkerTileResult = {
Expand Down
8 changes: 6 additions & 2 deletions src/style-spec/reference/v8.json
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@
"type": "enum",
"values": {
"raster-dem": {
"doc": "A raster DEM source using Mapbox Terrain RGB"
"doc": "A RGB-encoded raster DEM source"
}
},
"doc": "The type of the source."
Expand Down Expand Up @@ -272,6 +272,10 @@
"type": "string",
"doc": "Contains an attribution to be displayed when the map is shown to a user."
},
"encoding": {
"type": "string",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this should be "type": "enum" with a values object listing the possible types.

"doc": "The encoding used by this source. Mapbox Terrain RGB is used by default. Supported encodings are `mapbox` and `mapzen`."
},
"*": {
"type": "*",
"doc": "Other keys to configure the data source."
Expand Down Expand Up @@ -502,7 +506,7 @@
}
},
"hillshade": {
"doc": "Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB tiles",
"doc": "Client-side hillshading visualization based on DEM data. Currently, the implementation only supports Mapbox Terrain RGB and Mapzen Terrarium tiles.",
"sdk-support": {
"basic functionality": {
"js": "0.43.0"
Expand Down