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 #6110

Merged
merged 14 commits into from
Feb 12, 2018
104 changes: 100 additions & 4 deletions debug/hillshade.html
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,52 @@
</head>

<body>

<style>
#menu {
background: #fff;
position: absolute;
z-index: 1;
top: 10px;
right: 10px;
border-radius: 3px;
width: 120px;
border: 1px solid rgba(0,0,0,0.4);
font-family: 'Open Sans', sans-serif;
}

#menu a {
font-size: 13px;
color: #404040;
display: block;
margin: 0;
padding: 0;
padding: 10px;
text-decoration: none;
border-bottom: 1px solid rgba(0,0,0,0.25);
text-align: center;
}

#menu a:last-child {
border: none;
}

#menu a:hover {
background-color: #f8f8f8;
color: #404040;
}

#menu a.active {
background-color: #3887be;
color: #ffffff;
}

#menu a.active:hover {
background: #3074a4;
}
</style>

<nav id="menu"></nav>
<div id='map'></div>

<script src='/dist/mapbox-gl-dev.js'></script>
Expand All @@ -26,19 +72,69 @@
});

map.on('load', function () {
map.addSource('dem', {

map.addSource('mapbox-dem', {
"type": "raster-dem",
"url": "mapbox://mapbox.terrain-rgb"
"url": "mapbox://mapbox.terrain-rgb",
"tileSize": 256
});
map.addLayer({
"id": "hillshading",
"source": "dem",
"id": "Mapbox data",
"source": "mapbox-dem",
"type": "hillshade"
// insert below waterway-river-canal-shadow;
// where hillshading sits in the Mapbox Outdoors style
}, 'waterway-river-canal-shadow');

map.addSource('terrarium-dem', {
"type": "raster-dem",
"encoding": "terrarium",
"tiles": [
"https://s3.amazonaws.com/elevation-tiles-prod/terrarium/{z}/{x}/{y}.png"
],
"tileSize": 256
});
map.addLayer({
"id": "Terrarium data",
"source": "terrarium-dem",
"type": "hillshade",
"layout": {
"visibility": "none"
}
// insert below waterway-river-canal-shadow;
// where hillshading sits in the Mapbox Outdoors style
}, 'waterway-river-canal-shadow');

});

var toggleableLayerIds = ['Mapbox data', 'Terrarium data'];

for (var i = 0; i < toggleableLayerIds.length; i++) {
var id = toggleableLayerIds[i];

var link = document.createElement('a');
link.href = '#';
link.className = (i === 0) ? 'active' : '';
link.textContent = id;

link.onclick = function (e) {
var clickedLayer = this.textContent;
e.preventDefault();
e.stopPropagation();

if (this.className === '') {
var activeLayer = document.getElementsByClassName('active')[0];
activeLayer.className = '';
map.setLayoutProperty(activeLayer.textContent, 'visibility', 'none');
this.className = 'active';
map.setLayoutProperty(clickedLayer, 'visibility', 'visible');
}
};

var layers = document.getElementById('menu');
layers.appendChild(link);
}

</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"?: "terrarium" | "mapbox"
}

declare type GeojsonSourceSpecification = {|
Expand Down
38 changes: 27 additions & 11 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: "mapbox" | "terrarium") {
if (data.height !== data.width) throw new RangeError('DEM tiles must be square');

if (encoding && encoding !== "mapbox" && encoding !== "terrarium") return util.warnOnce(`"${encoding}" is not a valid encoding type. Valid types include "mapbox" and "terrarium".`);
// 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, pixels, encoding || "mapbox");

// 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,30 @@ class DEMData {
this.loaded = true;
}

_unpackMapbox(r: number, g: number, b: number) {
// unpacking formula for mapbox.terrain-rgb:
// https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb
return ((r * 256 * 256 + g * 256.0 + b) / 10.0 - 10000.0);
}

_unpackTerrarium(r: number, g: number, b: number) {
// unpacking formula for mapzen terrarium:
// https://aws.amazon.com/public-datasets/terrain/
return ((r * 256 + g + b / 256) - 32768.0);
}

_unpackData(level: Level, pixels: Uint8Array | Uint8ClampedArray, encoding: string) {
const unpackFunctions = {"mapbox": this._unpackMapbox, "terrarium": this._unpackTerrarium};
const unpack = unpackFunctions[encoding];
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;
level.set(x, y, this.scale * unpack(pixels[j], pixels[j + 1], pixels[j + 2]));
}
}
}

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: "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 = 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
5 changes: 3 additions & 2 deletions src/source/raster_dem_tile_worker_source.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,12 @@ class RasterDEMTileWorkerSource {
}

loadTile(params: WorkerDEMTileParameters, callback: WorkerDEMTileCallback) {
const uid = params.uid;
const uid = params.uid,
encoding = params.encoding;

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

this.loaded = this.loaded || {};
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 @@ -28,7 +28,8 @@ export type WorkerTileParameters = TileParameters & {

export type WorkerDEMTileParameters = TileParameters & {
coord: { z: number, x: number, y: number, w: number },
rawImageData: RGBAImage
rawImageData: RGBAImage,
encoding: "mapbox" | "terrarium"
};

export type WorkerTileResult = {
Expand Down
17 changes: 15 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,19 @@
"type": "string",
"doc": "Contains an attribution to be displayed when the map is shown to a user."
},
"encoding": {
"type": "enum",
"values": {
"terrarium": {
"doc": "Terrarium format PNG tiles. See https://aws.amazon.com/es/public-datasets/terrain/ for more info."
},
"mapbox": {
"doc": "Mapbox Terrain RGB tiles. See https://www.mapbox.com/help/access-elevation-data/#mapbox-terrain-rgb for more info."
}
},
"default": "mapbox",
"doc": "The encoding used by this source. Mapbox Terrain RGB is used by default"
},
"*": {
"type": "*",
"doc": "Other keys to configure the data source."
Expand Down Expand Up @@ -502,7 +515,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
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"version": 8,
"metadata": {
"test": {
"height": 64,
"width": 64
}
},
"center": [-113.26903, 35.995],
"zoom": 11,
"sources": {
"source": {
"type": "raster-dem",
"tiles": [
"local://tiles/{z}-{x}-{y}.terrarium.png"
],
"maxzoom": 15,
"tileSize": 256,
"encoding": "terrarium"
}
},
"layers": [
{
"id": "background",
"type": "background",
"paint": {
"background-color": "white"
}
},
{
"id": "hillshade",
"type": "hillshade",
"source": "source"
}
]
}
Binary file added test/integration/tiles/12-759-1608.terrarium.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
15 changes: 14 additions & 1 deletion test/unit/data/dem_data.test.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
'use strict';

const test = require('mapbox-gl-js-test').test;
const util = require('../../../src/util/util');
const {DEMData, Level} = require('../../../src/data/dem_data');
const {RGBAImage} = require('../../../src/util/image');
const {serialize, deserialize} = require('../../../src/util/web_worker_transfer');
Expand Down Expand Up @@ -48,7 +49,7 @@ test('Level', (t)=>{
});


test('DEMData constructor', (t) => {
test('DEMData', (t) => {
t.test('constructor', (t) => {
const dem = new DEMData(0, 1);
t.false(dem.loaded);
Expand Down Expand Up @@ -77,6 +78,18 @@ test('DEMData constructor', (t) => {
t.end();
});

t.test('loadFromImage with invalid encoding', (t) => {
const dem = new DEMData(0, 1);
t.stub(util, 'warnOnce');
t.false(dem.loaded);
t.equal(dem.uid, 0);

dem.loadFromImage({width: 4, height: 4, data: new Uint8ClampedArray(4 * 4 * 4)}, "derp");
t.ok(util.warnOnce.calledOnce);
t.ok(util.warnOnce.getCall(0).calledWithMatch(/"derp" is not a valid encoding type/));
t.end();
});

t.end();
});

Expand Down