-
Notifications
You must be signed in to change notification settings - Fork 3.5k
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
Fix sampleTerrain when using ArcGIS Terrain #9286
Changes from 12 commits
c2d0e53
93605ad
96b24e2
2a25258
1b6f86b
3097f21
3193628
3be316d
f9d5a09
95490b9
1c51236
7bffb5a
a1a9d54
67001cd
ccf37c1
3bb6330
c813c23
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -32,3 +32,4 @@ yarn.lock | |
# WebStorm user-specific | ||
.idea/workspace.xml | ||
.idea/tasks.xml | ||
.idea/shelf |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -304,7 +304,14 @@ Object.defineProperties(ArcGISTiledElevationTerrainProvider.prototype, { | |
*/ | ||
availability: { | ||
get: function () { | ||
return undefined; | ||
//>>includeStart('debug', pragmas.debug) | ||
if (!this._ready) { | ||
throw new DeveloperError( | ||
"availability must not be called before the terrain provider is ready." | ||
); | ||
} | ||
//>>includeEnd('debug'); | ||
return this._tilesAvailable; | ||
}, | ||
}, | ||
}); | ||
|
@@ -650,6 +657,7 @@ function requestAvailability(that, level, x, y) { | |
|
||
// Mark whole area as having availability loaded | ||
that._tilesAvailablityLoaded.addAvailableTileRange( | ||
level, | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I just saw this - it looked like a missing parameter - I've got no tests or further context on this one! 😛 Will revert later. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 😮 There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. this makes me question |
||
xOffset, | ||
yOffset, | ||
xOffset + dim, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,6 +1,9 @@ | ||
import { ArcGISTiledElevationTerrainProvider } from "../../Source/Cesium.js"; | ||
import { Cartographic } from "../../Source/Cesium.js"; | ||
import { CesiumTerrainProvider } from "../../Source/Cesium.js"; | ||
import { createWorldTerrain } from "../../Source/Cesium.js"; | ||
import { RequestScheduler } from "../../Source/Cesium.js"; | ||
import { Resource } from "../../Source/Cesium.js"; | ||
import { sampleTerrain } from "../../Source/Cesium.js"; | ||
|
||
describe("Core/sampleTerrain", function () { | ||
|
@@ -99,4 +102,192 @@ describe("Core/sampleTerrain", function () { | |
expect(positions[0].height).toBeDefined(); | ||
}); | ||
}); | ||
|
||
describe("with terrain providers", function () { | ||
beforeEach(function () { | ||
RequestScheduler.clearForSpecs(); | ||
}); | ||
|
||
afterEach(function () { | ||
Resource._Implementations.loadWithXhr = | ||
Resource._DefaultImplementations.loadWithXhr; | ||
}); | ||
|
||
function spyOnTerrainDataCreateMesh(terrainProvider) { | ||
// do some sneaky spying so we can check how many times createMesh is called | ||
var originalRequestTileGeometry = terrainProvider.requestTileGeometry; | ||
spyOn(terrainProvider, "requestTileGeometry").and.callFake(function ( | ||
x, | ||
y, | ||
level, | ||
request | ||
) { | ||
// Call the original function! | ||
return originalRequestTileGeometry | ||
.call(terrainProvider, x, y, level, request) | ||
.then(function (tile) { | ||
spyOn(tile, "createMesh").and.callThrough(); | ||
// return the original tile - after we've spied on the create mesh method | ||
return tile; | ||
}); | ||
}); | ||
} | ||
|
||
function expectTileAndMeshCounts( | ||
terrainProvider, | ||
numberOfTilesRequested, | ||
wasFirstTileMeshCreated | ||
) { | ||
// all came from a single tile | ||
expect(terrainProvider.requestTileGeometry.calls.count()).toEqual( | ||
numberOfTilesRequested | ||
); | ||
|
||
// get the return terrain data for our spies | ||
return ( | ||
terrainProvider.requestTileGeometry.calls | ||
.first() | ||
// return value was the promise of the TerrainData | ||
.returnValue.then(function (terrainData) { | ||
// make sure the mesh was only created once! | ||
expect(terrainData.createMesh.calls.count()).toEqual( | ||
wasFirstTileMeshCreated ? 1 : 0 | ||
); | ||
}) | ||
); | ||
} | ||
|
||
function endsWith(value, suffix) { | ||
return value.indexOf(suffix, value.length - suffix.length) >= 0; | ||
} | ||
|
||
function patchXHRLoad(proxySpec) { | ||
Resource._Implementations.loadWithXhr = function ( | ||
url, | ||
responseType, | ||
method, | ||
data, | ||
headers, | ||
deferred, | ||
overrideMimeType | ||
) { | ||
// find a key (source path) path in the spec which matches (ends with) the requested url | ||
var availablePaths = Object.keys(proxySpec); | ||
var proxiedUrl = null; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do |
||
|
||
for (var i = 0; i < availablePaths.length; i++) { | ||
var srcPath = availablePaths[i]; | ||
if (endsWith(url, srcPath)) { | ||
proxiedUrl = proxySpec[availablePaths[i]]; | ||
break; | ||
} | ||
} | ||
|
||
// it's a whitelist - meaning you have to proxy every request explicitly | ||
if (!proxiedUrl) { | ||
throw new Error( | ||
"Unexpected XHR load to url: " + | ||
url + | ||
"; spec includes: " + | ||
availablePaths.join(", ") | ||
); | ||
} | ||
|
||
// make a real request to the proxied path for the matching source path | ||
return Resource._DefaultImplementations.loadWithXhr( | ||
proxiedUrl, | ||
responseType, | ||
method, | ||
data, | ||
headers, | ||
deferred, | ||
overrideMimeType | ||
); | ||
}; | ||
} | ||
|
||
it("should work for Cesium World Terrain", function () { | ||
patchXHRLoad({ | ||
"/layer.json": "Data/CesiumTerrainTileJson/9_759_335/layer.json", | ||
"/9/759/335.terrain?v=1.2.0": | ||
"Data/CesiumTerrainTileJson/9_759_335/9_759_335.terrain", | ||
}); | ||
var terrainProvider = new CesiumTerrainProvider({ | ||
url: "made/up/url", | ||
}); | ||
|
||
spyOnTerrainDataCreateMesh(terrainProvider); | ||
|
||
var positionA = Cartographic.fromDegrees( | ||
86.93666235421982, | ||
27.97989963555095 | ||
); | ||
var positionB = Cartographic.fromDegrees( | ||
86.9366623542198, | ||
27.9798996355509 | ||
); | ||
var positionC = Cartographic.fromDegrees( | ||
86.936662354213, | ||
27.979899635557 | ||
); | ||
|
||
var level = 9; | ||
|
||
return sampleTerrain(terrainProvider, level, [ | ||
positionA, | ||
positionB, | ||
positionC, | ||
]).then(function () { | ||
expect(positionA.height).toBeCloseTo(7780, 0); | ||
expect(positionB.height).toBeCloseTo(7780, 0); | ||
expect(positionC.height).toBeCloseTo(7780, 0); | ||
// 1 tile was requested (all positions were close enough on the same tile) | ||
// and the mesh was not created because we're using CWT - which doesn't need the mesh for interpolation | ||
return expectTileAndMeshCounts(terrainProvider, 1, false); | ||
}); | ||
}); | ||
|
||
it("should work for ArcGIS terrain", function () { | ||
patchXHRLoad({ | ||
"/?f=pjson": "Data/ArcGIS/9_214_379/root.json", | ||
"/tilemap/10/384/640/128/128": | ||
"Data/ArcGIS/9_214_379/tilemap_10_384_640_128_128.json", | ||
"/tile/9/214/379": "Data/ArcGIS/9_214_379/tile_9_214_379.tile", | ||
}); | ||
|
||
var terrainProvider = new ArcGISTiledElevationTerrainProvider({ | ||
url: "made/up/url", | ||
}); | ||
|
||
spyOnTerrainDataCreateMesh(terrainProvider); | ||
|
||
var positionA = Cartographic.fromDegrees( | ||
86.93666235421982, | ||
27.97989963555095 | ||
); | ||
var positionB = Cartographic.fromDegrees( | ||
86.9366623542198, | ||
27.9798996355509 | ||
); | ||
var positionC = Cartographic.fromDegrees( | ||
86.936662354213, | ||
27.979899635557 | ||
); | ||
|
||
var level = 9; | ||
return sampleTerrain(terrainProvider, level, [ | ||
positionA, | ||
positionB, | ||
positionC, | ||
]).then(function () { | ||
// 3 very similar positions | ||
expect(positionA.height).toBeCloseTo(7681, 0); | ||
expect(positionB.height).toBeCloseTo(7681, 0); | ||
expect(positionC.height).toBeCloseTo(7681, 0); | ||
// 1 tile was requested (all positions were close enough on the same tile) | ||
// and the mesh was created once because we're using an ArcGIS tile | ||
return expectTileAndMeshCounts(terrainProvider, 1, true); | ||
}); | ||
}); | ||
}); | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was the other fix mentioned... I'm not really across how the
TileAvailability
class works and if this_tilesAvailable
property is setup correctly. I did notice there's actually two properties_tilesAvailablityLoaded
and_tilesAvailable
in the ArcGIS Terrain Provider; not sure which one is correct in this case.Either way, I think this makes
sampleTerrainMostDetailed
work as expected 👍There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
yeah, this seems like the right thing to return. I'm still wrapping my head around
_tilesAvailablityLoaded
. I think it only says whether availability has been loaded for tiles, not whether a tile is actually available. Not sure why_tilesAvailable
can't do take care of this, but there's probably a good reason.