Last active
February 16, 2024 00:11
-
-
Save erhhung/20b0119527b6b76a0f0e669fd1cfed78 to your computer and use it in GitHub Desktop.
Cesium Sandcastle Icon Scaling Issue
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* 1. open https://sandcastle.cesium.com/ | |
* 2. paste this code into the editor | |
* 3. open debugger and disable cache | |
* 4. click Run and wait for globe | |
* 5. click "Run camera sequence" | |
*/ | |
const URL = 'https://geotest{s}.ixstack.net/wms'; | |
const LAYER = 'gvdl:eethnhgcjbwvsrwaqfsgyvzzkw'; | |
const STYLE = 'gvdl:point'; | |
const ICON = '//gvdldev1.ixstack.net/resources/images/MBLY_traffic_lightsX.svg'; | |
const VIEWS = [ | |
{bbox:[35.1776846357, 31.7591124942, 35.2591994373, 31.8057649301], delay:1}, | |
{bbox:[35.2239844326, 31.7781897108, 35.2270145363, 31.7799243770], delay:5}, | |
{bbox:[35.2031740007, 31.7863955220, 35.2046575848, 31.7872447724], delay:5}, | |
]; | |
const SUBDOMAINS = 3; | |
const wmsArgs = { | |
url: new Cesium.Resource({ | |
url: URL, | |
headers: { | |
// NOTE: this Cesium Support access token expires on 2021-07-25 | |
Authorization: 'Bearer eyJhbGciOiJIUzI1NiIsImtpZCI6IjZ4eXBwbWo1ZG44aGtzNGozc2VldGozdGoyLmtpZCIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJpZ3A6ZGV2MSIsImF1ZCI6IndtcyIsInN1YiI6IjJiMW5zd3l0Z2Y4cjBhcXJhODZ4NTdkeTNhLmFwcCIsImFwcCI6ImNlc2l1bS1zdXBwb3J0IiwiY29tIjoidzUxN2ZqZGtzODZldGFudzE4MjNkN3ZrNHcuY29tIiwianRpIjoiN2U4OHlmempwNTh3bnQ1eHZiZDdjd3dtdG4uanRpIiwiaWF0IjoxNjI0NjQwNDAwLCJleHAiOjE2MjcyMzI0MDB9.fpapCEwd5qm4Au-0dbHrB06ekn93DyBD_EvakISCdB4', | |
}, | |
}), | |
subdomains: Array.from({length: SUBDOMAINS}, (v,i) => `${i+1}`), | |
layers: LAYER, | |
}; | |
const params = { | |
tiled: true, | |
transparent: true, | |
format: 'image/png8', | |
//format_options: 'antialias:off', | |
styles: STYLE, | |
}; | |
// https://www.cesium.com/docs/cesiumjs-ref-doc/WebMapServiceImageryProvider.html | |
const createProvider = opts => new Cesium.WebMapServiceImageryProvider({ | |
...wmsArgs, | |
parameters: { | |
...params, | |
...opts, | |
}, | |
getFeatureInfoParameters: { | |
// don't need all "params" | |
styles: STYLE, | |
...opts, | |
}, | |
}); | |
// env: takes array of sublayer tuples and optional | |
// property name (style defines default: "category") | |
const env = (subs, prop='') => (prop ? `prop:${prop};`:'') + | |
subs.map(([v,c,o,u],i) => `v${i}:${v}` + | |
`;c${i}:${c}` + | |
`;o${i}:${o}` + | |
(u ?`;u${i}:${u}`:'')).join(';'); | |
const provider = createProvider({ | |
env: env([ | |
// [value, color, opacity, url] | |
['Unchanged','D4AEFA', 1, ICON], | |
['Changed', 'FFC887', 1, ICON], | |
['New', 'D9FF87', 1, ICON], | |
['Missing', 'FF7070', 1, ICON], | |
]), | |
}); | |
// https://cesium.com/learn/cesiumjs/ref-doc/Viewer.html#.ConstructorOptions | |
const viewer = new Cesium.Viewer('cesiumContainer', { | |
geocoder: false, | |
sceneModePicker: false, | |
baseLayerPicker: false, | |
navigationHelpButton: false, | |
animation: false, | |
timeline: false, | |
}); | |
// https://cesium.com/learn/cesiumjs/ref-doc/ImageryLayer.html | |
const layer = new Cesium.ImageryLayer(provider, { | |
// set zoom level below which layer is hidden | |
// this value doesn't appear to be adjustable | |
minimumTerrainLevel: 0, | |
}); | |
viewer.imageryLayers.add(layer); | |
let timeout; | |
const debug = { | |
hideLayerDuringFlight: false, | |
}; | |
function setCameraView(viewRect) { | |
// hide layer during the flyTo animation | |
layer.show = !debug.hideLayerDuringFlight; | |
// https://cesium.com/learn/cesiumjs/ref-doc/Camera.html#flyTo | |
viewer.camera.flyTo({ | |
destination: viewRect, | |
complete: () => layer.show = true, | |
}); | |
} | |
function flyToView(i=0) { | |
const {bbox,delay} = VIEWS[i]; | |
timeout = setTimeout(() => { | |
setCameraView(Cesium.Rectangle.fromDegrees(...bbox)); | |
const view = bbox.map(x => x.toFixed(10)).join(', '); | |
console.log(`Flying to VIEWS[${i}]: ${view}`); | |
if (VIEWS[i+1]) { | |
flyToView(i+1); | |
} | |
}, delay*1000); | |
} | |
function runSequence() { | |
const {camera} = viewer; | |
camera.cancelFlight(); | |
if (timeout) { | |
camera.setView({destination: Cesium.Camera.DEFAULT_VIEW_RECTANGLE}); | |
camera.zoomOut(8575000); | |
clearTimeout(timeout); | |
} | |
flyToView(); | |
} | |
/* | |
* RENDER HUD AND CONTROLS | |
*/ | |
document.getElementById('toolbar').outerHTML = `<div id="toolbar"/><div id="hud"/>`; | |
const hud = document.getElementById('hud'); | |
// https://github.com/CesiumGS/cesium/blob/master/Apps/Sandcastle/Sandcastle-header.js | |
Sandcastle.addToolbarButton('Run camera sequence', runSequence); | |
Sandcastle.addToggleButton('Hide layer during flight', debug.hideLayerDuringFlight, | |
checked => debug.hideLayerDuringFlight = checked); | |
const elts = document.getElementsByClassName('cesium-button'); | |
for (let i=0; i < elts.length; i++) { | |
// fix uneven button heights | |
elts[i].style.height = '28px'; | |
} | |
// add thousands separators to number | |
const addSep = x => String(x).replace(/\B(?=(\d{3})+(?!\d))(?<!\..*)/g,','); | |
const LEVELS = Array.from({length: 24}, (v,i) => 156543.034 / 2**i); // m/px | |
function getCameraView() { | |
const rect = viewer.camera.computeViewRectangle(); | |
const rads = []; Cesium.Rectangle.pack(rect, rads); | |
const degs = rads.map(r => Cesium.Math.toDegrees(r)); | |
return degs.map(d => d.toFixed(10)).join(', '); | |
} | |
function getCameraPos() { | |
const posWC = viewer.camera.positionWC; | |
const carto = new Cesium.Cartographic(); | |
const {ellipsoid} = viewer.scene.mapProjection; | |
ellipsoid.cartesianToCartographic(posWC, carto); | |
return { | |
lon: Cesium.Math.toDegrees(carto.longitude).toFixed(3), | |
lat: Cesium.Math.toDegrees(carto.latitude).toFixed(3), | |
alt: (carto.height / 1000).toFixed(3), | |
}; | |
} | |
function getCameraZoom() { | |
const {ellipsoid} = viewer.scene.globe; | |
const {camera,canvas} = viewer.scene; | |
const {width:w, height:h} = canvas; | |
function getViewExt() { | |
const corners = [[0,0], [w,0], [0,h],[w,h]].map(p => | |
camera.pickEllipsoid(new Cesium.Cartesian2(...p), ellipsoid) | |
); | |
if (corners.some(x => !x)) { | |
return Cesium.Rectangle.MAX_VALUE; | |
} | |
return Cesium.Rectangle.fromCartographicArray( | |
ellipsoid.cartesianArrayToCartographicArray(corners) | |
); | |
} | |
const extent = getViewExt(); | |
if (extent.equals(Cesium.Rectangle.MAX_VALUE)) { | |
return {level:'?', scale:'∞'}; | |
} | |
const diff = Math.abs(extent.north - extent.south); | |
const kmPerPixel = (diff * 6355.3) / canvas.clientHeight; | |
const mPerPixel = kmPerPixel * 1000; | |
const cmPerPixel = mPerPixel * 100; | |
const scale = cmPerPixel * 96 / 2.54; | |
return { | |
level: LEVELS.findIndex(r => mPerPixel > (r + r/2) / 2), | |
scale: addSep(scale.toFixed()), | |
}; | |
} | |
// update HUD on clock ticks AND frame updates | |
viewer.clock.onTick.addEventListener(() => { | |
const view = getCameraView(); | |
const {lon,lat,alt} = getCameraPos(); | |
const {level,scale} = getCameraZoom(); | |
const data = view + lon+lat+alt + level+scale; | |
if (data === hud.data) { | |
return; | |
} | |
hud.data = data; | |
hud.innerHTML = `<table> | |
<tr><th align=right>VIEW:</th><td>${view}</td></tr> | |
<tr><th>CAMERA:</th><td>lon ${lon}°, lat ${lat}°, alt ${alt} km</td></tr> | |
<tr><th align=right>ZOOM:</th><td>${level} (1:${scale})</td></tr> | |
</table>`; | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment