3DStreet / 3dstreet

🚲🚶🚌 Web-based 3D visualization of streets using A-Frame
https://3dstreet.app
Other
256 stars 33 forks source link

setting elevation incorrect outside of west coast north america #655

Closed kfarr closed 2 months ago

kfarr commented 3 months ago

setting elevation of 3dtiles from google maps lat/long was incorrect – using a ‘constant’ to convert between is faulty for the following cases


To reproduce:

Why?

Question:

How to find the answer:

Related concepts:

kfarr commented 2 months ago

@bkovitz and I did some work this afternoon on using an old fashioned raycaster to find the ground

Here is a quick video of the attempt showing raycaster events for a box geometry but not the 3dtiles: https://github.com/nytimes/three-loader-3dtiles/assets/470477/a98ed4ad-2b32-4796-93c5-88d4dfee0ea2

Here is a link to the WIP raycaster demo that does not work yet... https://3dstreet.github.io/aframe-loader-3dtiles-component/examples/google-tiles-raycaster/

https://github.com/3DStreet/aframe-loader-3dtiles-component/blob/dev/examples/google-tiles-raycaster/index.html#L60

Instead, we may need to go lower level. This is a key part of the draping example. https://github.com/nytimes/three-loader-3dtiles/blob/dev/src/draping.ts#L148

A super hacky hack could be creating a bespoke geojson with 1 point or a polygon around the same centerpoint as the scene long/lat, then use the working drape function from avner, then find a vertex from that polygon and locate it

looking at the a-frame raycaster component, it only intersects with elements in the a-frame scene graph (dom): https://github.com/aframevr/aframe/blob/ca7b02ddd7bfa9d606cbbe7b092eb0c08afe5728/src/components/raycaster.js#L183

So perhaps after getting objects on line 185, we can then add all of the children object3d from the visible 3dtiles, see here: https://github.com/nytimes/three-loader-3dtiles/issues/97

Algorush commented 2 months ago

I read this thread. Is it okay if I try to resolve this issue using the method suggested by Avnerus? Convert height from EGM96 Geoid format to desired format using Math.gl library? Maybe it would be easier

Algorush commented 2 months ago

In the process of my searches, I found that the height in the Google Maps API is indeed transmitted in the GM96 format, while the coordinates are in the WGS84 format. I also discovered that it is possible to get elevation information by coordinates in WGS84 format through other APIs such as: Open Elevation API, NASA, USGS Elevation Point Query Service. Or we can convert the height from EGM96 to WGS 84 using the egm96-universal translator (https://www.npmjs.com/package/egm96-universal) or proj4 library, for example. But transator is heavy (2,5mb).

bkovitz commented 2 months ago

Alexander, that sounds like promising info: that the elevation is EGM96 but the coordinates are WGS84 (very surprising!). If you could get that to work, it would be a principled solution. The solution that @kfarr and I put in on Saturday is somewhat unprincipled but it's very simple—just a few lines of code. It "empirically" finds the ground by doing a raycast from a high elevation straight down and seeing what it intersects. This avoids the problem that elevation databases can have inaccuracies, but it might still go wrong because tiles of different Levels of Detail get loaded into memory gradually and the raycaster might find a huge, coarse tile and therefore misjudge the elevation.

It might be worthwhile to implement the EGM96–WGS84 translation and then try out both methods at a variety of different geographical locations to see which method is truly reliable. All that really matters, of course, is that the eyepoint of the camera start out somewhat above ground. Even hundreds of meters above ground is OK, but below ground is not OK.

kfarr commented 2 months ago

@bkovitz @Algorush I also spent some time last night on the ability to do this via math instead of raycaster. Ben and I feared that the Ground Elevation Model would require interpolation and therefore be inaccurate. However, in my testing the accuracy is plenty good, but the remaining issue is that it uses a large GEM dataset either on the client (minimum 500k up to many megabytes) or needs to be a backend service that we or others host.

Here are 2 precise examples to validate this method. I used the NOAA API however that only works for US locations. There does not appear to be a simple / free / open api like this for all global lat/long points.

Rural location in Montana:

Hetch Hetchy reservoir / Yosemite:

A few options to proceed: 1) refine the "empirical" method that Ben and I created 2) explore creating a cloud function with the GEM model in memory so that we can query it

I vote to continue (1)

Algorush commented 2 months ago

I can also try to make a cloud (Firebase?) function that will accept lat, long and return the height in WGS84 to the request. The function will make requests to Google Maps for the height in EGM96, convert it to WGS84 using a ready-made library (edm-universal) or I can write my own version with proj4 or Math.gl and return the height in WGS84 to the request

kfarr commented 2 months ago

Ok I have a PR for review: https://github.com/3DStreet/3dstreet/pull/720

On a sidenote, I used the local firebase emulator which was helpful to avoid waiting for time to deploy firebase function to dev server:

I also deployed on dev server: https://us-central1-dev-3dstreet.cloudfunctions.net/getGeoidHeight?lat=48.4069835&lon=-114.3008482

compare response to: https://geodesy.noaa.gov/api/geoid/ght?lat=48.4069835&lon=-114.3008482

it's within a half a meter accuracy for that long/lat, that's plenty great and we can add a few meters to be safe? (to ensure that we're always above ground and not below ground?)

but we should test a few more lat/long locations

next steps: