Skip to content

Instantly share code, notes, and snippets.

@fasiha
Created October 20, 2015 18:49
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save fasiha/63d2dbb36fc88f72c078 to your computer and use it in GitHub Desktop.
Save fasiha/63d2dbb36fc88f72c078 to your computer and use it in GitHub Desktop.
Imagery Layers Manipulation demo for Cesium.js using custom image processing: try at http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Imagery%20Layers%20Manipulation.html
// See discussion at https://groups.google.com/d/msg/cesium-dev/aysBQWn9c88/JoR0dpQ_DAAJ
// Paste this into http://cesiumjs.org/Cesium/Apps/Sandcastle/index.html?src=Imagery%20Layers%20Manipulation.html, then look at the Cesium logo hovering above Utah, USA (it's colors have been edited)
var defined = Cesium.defined;
var when = Cesium.when;
var DeveloperError = Cesium.DeveloperError;
var ImageryProviderHooks = {};
ImageryProviderHooks.addRecolorFunc = function(imageryProvider, recolorFunc) {
// Override requestImage to recolor the images.
imageryProvider.base_requestImage = imageryProvider.requestImage;
imageryProvider.recolorFunc = recolorFunc;
imageryProvider.requestImage = function(x, y, level) {
var imagePromise = this.base_requestImage(x, y, level);
if (!defined(imagePromise)) {
return imagePromise;
}
var that = this;
return when(imagePromise, function(image) {
if (defined(image)) {
var context = getCanvasContext(imageryProvider, image);
image = recolorImageWithCanvasContext(context, image, that.recolorFunc);
}
return image;
});
};
};
/* Recolor a raster image pixel by pixel, replacing encoded identifiers with some calculated value. */
ImageryProviderHooks.recolorImage = function(image, colorFunc) {
var length = image.data.length; //pixel count * 4
for (var i = 0; i < length; i += 4) {
// Convert that identifier into the data-mapped colour it should display as.
var clr = colorFunc(image.data[i], image.data[i+1], image.data[i+2], image.data[i+3]);
if (defined(clr)) {
for (var j = 0; j < 4; j++) {
image.data[i+j] = clr[j];
}
}
}
return image;
};
function getCanvasContext(imageryProvider, img) {
var context = imageryProvider._canvas2dContext;
if (!defined(context) || context.canvas.width !== img.width || context.canvas.height !== img.height) {
var canvas = document.createElement("canvas");
canvas.width = img.width;
canvas.height = img.height;
context = canvas.getContext("2d");
imageryProvider._canvas2dContext = context;
}
return context;
}
/* Copy an image to a newly created Canvas, then perform recoloring there. */
function recolorImageWithCanvasContext(context, img, colorFunc) {
if (!defined(context)) {
throw new DeveloperError('No context for image recoloring.');
}
// Copy the image contents to the canvas
context.clearRect(0, 0, context.canvas.width, context.canvas.height);
context.drawImage(img, 0, 0);
var image = context.getImageData(0, 0, context.canvas.width, context.canvas.height);
image = ImageryProviderHooks.recolorImage(image, colorFunc);
return image;
}
ImageryProviderHooks.addPickFeaturesHook = function(imageryProvider, hook) {
// Override pickFeatures to add more metadata.
imageryProvider.base_pickFeatures = imageryProvider.pickFeatures;
imageryProvider.pickFeatures = function(x, y, level, longitude, latitude) {
var featurePromise = this.base_pickFeatures(x, y, level, longitude, latitude);
if (!defined(featurePromise)) {
return featurePromise;
}
return featurePromise.then(hook);
};
};
///////////////////
var viewer = new Cesium.Viewer('cesiumContainer', {
baseLayerPicker : false
});
var imageryLayers = viewer.imageryLayers;
var viewModel = {
layers : [],
baseLayers : [],
upLayer : null,
downLayer : null,
selectedLayer : null,
isSelectableLayer : function(layer) {
return baseLayers.indexOf(layer) >= 0;
},
raise : function(layer, index) {
imageryLayers.raise(layer);
viewModel.upLayer = layer;
viewModel.downLayer = viewModel.layers[Math.max(0, index - 1)];
updateLayerList();
window.setTimeout(function() { viewModel.upLayer = viewModel.downLayer = null; }, 10);
},
lower : function(layer, index) {
imageryLayers.lower(layer);
viewModel.upLayer = viewModel.layers[Math.min(viewModel.layers.length - 1, index + 1)];
viewModel.downLayer = layer;
updateLayerList();
window.setTimeout(function() { viewModel.upLayer = viewModel.downLayer = null; }, 10);
},
canRaise : function(layerIndex) {
return layerIndex > 0;
},
canLower : function(layerIndex) {
return layerIndex >= 0 && layerIndex < imageryLayers.length - 1;
}
};
Cesium.knockout.track(viewModel);
var baseLayers = viewModel.baseLayers;
function setupLayers() {
// Create all the base layers that this example will support.
// These base layers aren't really special. It's possible to have multiple of them
// enabled at once, just like the other layers, but it doesn't make much sense because
// all of these layers cover the entire globe and are opaque.
addBaseLayerOption(
'Bing Maps Aerial',
undefined); // the current base layer
addBaseLayerOption(
'Bing Maps Road',
new Cesium.BingMapsImageryProvider({
url: '//dev.virtualearth.net',
mapStyle: Cesium.BingMapsStyle.ROAD
}));
addBaseLayerOption(
'ArcGIS World Street Maps',
new Cesium.ArcGisMapServerImageryProvider({
url : '//server.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer'
}));
addBaseLayerOption(
'OpenStreetMaps',
new Cesium.OpenStreetMapImageryProvider());
addBaseLayerOption(
'MapQuest OpenStreetMaps',
new Cesium.OpenStreetMapImageryProvider({
url: '//otile1-s.mqcdn.com/tiles/1.0.0/osm/'
}));
addBaseLayerOption(
'Stamen Maps',
new Cesium.OpenStreetMapImageryProvider({
url: '//stamen-tiles.a.ssl.fastly.net/watercolor/',
fileExtension: 'jpg',
credit: 'Map tiles by Stamen Design, under CC BY 3.0. Data by OpenStreetMap, under CC BY SA.'
}));
addBaseLayerOption(
'Natural Earth II (local)',
new Cesium.TileMapServiceImageryProvider({
url : require.toUrl('Assets/Textures/NaturalEarthII')
}));
addBaseLayerOption(
'USGS Shaded Relief (via WMTS)',
new Cesium.WebMapTileServiceImageryProvider({
url : 'http://basemap.nationalmap.gov/arcgis/rest/services/USGSShadedReliefOnly/MapServer/WMTS',
layer : 'USGSShadedReliefOnly',
style : 'default',
format : 'image/jpeg',
tileMatrixSetID : 'default028mm',
maximumLevel: 19,
credit : new Cesium.Credit('U. S. Geological Survey')
}));
// Create the additional layers
addAdditionalLayerOption(
'United States GOES Infrared',
new Cesium.WebMapServiceImageryProvider({
url : '//mesonet.agron.iastate.edu/cgi-bin/wms/goes/conus_ir.cgi?',
layers : 'goes_conus_ir',
credit : 'Infrared data courtesy Iowa Environmental Mesonet',
parameters : {
transparent : 'true',
format : 'image/png'
},
proxy : new Cesium.DefaultProxy('/proxy/')
}));
addAdditionalLayerOption(
'United States Weather Radar',
new Cesium.WebMapServiceImageryProvider({
url : '//mesonet.agron.iastate.edu/cgi-bin/wms/nexrad/n0r.cgi?',
layers : 'nexrad-n0r',
credit : 'Radar data courtesy Iowa Environmental Mesonet',
parameters : {
transparent : 'true',
format : 'image/png'
},
proxy : new Cesium.DefaultProxy('/proxy/')
}));
addAdditionalLayerOption(
'TileMapService Image',
new Cesium.TileMapServiceImageryProvider({
url : '../images/cesium_maptiler/Cesium_Logo_Color'
}),
0.2);
var singleTile = new Cesium.SingleTileImageryProvider({
url : '../images/Cesium_Logo_overlay.png',
rectangle : Cesium.Rectangle.fromDegrees(-115.0, 38.0, -107, 39.75)
});
ImageryProviderHooks.addRecolorFunc(singleTile, (r,g,b,a) => [255-r, 255-g, 255-b, a]);
addAdditionalLayerOption(
'Single Image',
singleTile,
1.0);
addAdditionalLayerOption(
'Grid',
new Cesium.GridImageryProvider(), 1.0, false);
addAdditionalLayerOption(
'Tile Coordinates',
new Cesium.TileCoordinatesImageryProvider(), 1.0, false);
}
function addBaseLayerOption(name, imageryProvider) {
var layer;
if (typeof imageryProvider === 'undefined') {
layer = imageryLayers.get(0);
viewModel.selectedLayer = layer;
} else {
layer = new Cesium.ImageryLayer(imageryProvider);
}
layer.name = name;
baseLayers.push(layer);
}
function addAdditionalLayerOption(name, imageryProvider, alpha, show) {
var layer = imageryLayers.addImageryProvider(imageryProvider);
layer.alpha = Cesium.defaultValue(alpha, 0.5);
layer.show = Cesium.defaultValue(show, true);
layer.name = name;
Cesium.knockout.track(layer, ['alpha', 'show', 'name']);
}
function updateLayerList() {
var numLayers = imageryLayers.length;
viewModel.layers.splice(0, viewModel.layers.length);
for (var i = numLayers - 1; i >= 0; --i) {
viewModel.layers.push(imageryLayers.get(i));
}
}
setupLayers();
updateLayerList();
//Bind the viewModel to the DOM elements of the UI that call for it.
var toolbar = document.getElementById('toolbar');
Cesium.knockout.applyBindings(viewModel, toolbar);
Cesium.knockout.getObservable(viewModel, 'selectedLayer').subscribe(function(baseLayer) {
// Handle changes to the drop-down base layer selector.
var activeLayerIndex = 0;
var numLayers = viewModel.layers.length;
for (var i = 0; i < numLayers; ++i) {
if (viewModel.isSelectableLayer(viewModel.layers[i])) {
activeLayerIndex = i;
break;
}
}
var activeLayer = viewModel.layers[activeLayerIndex];
var show = activeLayer.show;
var alpha = activeLayer.alpha;
imageryLayers.remove(activeLayer, false);
imageryLayers.add(baseLayer, numLayers - activeLayerIndex - 1);
baseLayer.show = show;
baseLayer.alpha = alpha;
updateLayerList();
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment