CesiumGS / cesium

An open-source JavaScript library for world-class 3D globes and maps :earth_americas:
https://cesium.com/cesiumjs/
Apache License 2.0
12.68k stars 3.44k forks source link

Support historical imagery in `GoogleEarthEnterpriseImageryProvider.js` #12089

Open jo-chemla opened 1 month ago

jo-chemla commented 1 month ago

Feature

Some GoogleEarthEnterpriseImageryProvider support historical imagery TMS/XYZ - accessible via a timeline slider in Google Earth Entreprise - see doc. It would be great for the cesiumjs library to support accessing historical imagery from a Google Earth Entreprise instance.

Since the google/earthenterprise repo has been archived & open-sourced, some pointers can be found there as to how to build and parse historical imagery tiles.

Current implementation for a single TMS based on GE Entreprise quadkey

Here is how an imagery tile url is retrieved from a XYZ in the current cesiumjs codebase: https://github.com/CesiumGS/cesium/blob/1baec1dfd773f6a390266d73d155c7009936c4c7/packages/engine/Source/Scene/GoogleEarthEnterpriseImageryProvider.js#L450


function buildImageResource(imageryProvider, info, x, y, level, request) {
  const quadKey = GoogleEarthEnterpriseMetadata.tileXYToQuadKey(x, y, level);
  let version = info.imageryVersion;
  version = defined(version) && version > 0 ? version : 1;

  return imageryProvider._metadata.resource.getDerivedResource({
    url: `flatfile?f1-0${quadKey}-i.${version.toString()}`,
    request: request,
  });
}

Imagery Date formatting within url

Format

Here is the format that should be appended the imagery date to the request for it to work: simply append the imagery date to the url. It can be seen here url << server_ << "/flatfile?db=tm&f1-0" << qt_path.AsString() << "-i." << version << "-" << jpeg_date.GetHexString();

url: `flatfile?f1-0${quadKey}-i.${version.toString()}-${tileDate.toString()}`

Building the date hex string

// encodeDateToHex parses year, month, day to a hex string representing jpeg_date in gee format
const kBitsPerMonth = 4;
const kBitsPerDay = 5;
function encodeDateToHex(year, month, day) {
  let encodedDate = year;
  encodedDate = (encodedDate << kBitsPerMonth) | month;
  encodedDate = (encodedDate << kBitsPerDay) | day;
  return encodedDate.toString(16); // hexString
}
function decodeHexToDate(hexString) {
  // Convert hex string to integer
  let encodedDate = parseInt(hexString, 16);
  const day = encodedDate & 0x1F; // Extract the day (last 5 bits)
  encodedDate >>= kBitsPerDay;
  const month = encodedDate & 0x0F; // Extract the month (next 4 bits)
  encodedDate >>= kBitsPerMonth;
  const year = encodedDate;
  return { year, month, day };
}
ggetz commented 1 month ago

Hi @jo-chemla, thanks for the suggestion!

We haven't updated out GEE implementation in a while due to the service being deprecated, and also it has proven difficult to test on our end. This means there's some difficulty in maintaining a feature like this.

Are you able to able to move forward with a custom Imagery Provider specific to this case?

jo-chemla commented 1 month ago

Hi @ggetz, thanks for getting back! I was testing on a instance setup locally based on their doc with a few images over some archaeological sites we study.

I also used this sandcastle where I connect to Google own implementation via https://khmdb.google.com and https://kh.google.com. It does work great for standard (most recent) imagery, but I was not able to make it run for historical imagery by simply appending to the flatfile url the jpeg_date bit - tried with multiple dates that seem to be available over Paris at that zoom level, like encodeDateToHex(2019, 08, 22)) = 'fc716' etc. It might be possible that the current google implementation does not implement that historical flatfile feature - it does implement other flatfile urls though, hence why I think it should also work for historical.

So it looks like I'm stuck at the moment trying to parse through the google/earthenterprise repo code and cesiumjs codebase to mimick that historical imagery feature. Hopefully we can gather some help around here. Thanks a lot!

  const geeMetadata = await Cesium.GoogleEarthEnterpriseMetadata.fromUrl(gee_url);
  const gee = Cesium.GoogleEarthEnterpriseImageryProvider.fromMetadata(geeMetadata);
  const gee_imagery = new Cesium.ImageryLayer(gee);
  viewer.scene.imageryLayers.add(gee_imagery);