Spaces:
Sleeping
Sleeping
Upload get_height_gba.py
Browse files
height_predictor/get_height_gba.py
ADDED
|
@@ -0,0 +1,69 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
import math
|
| 2 |
+
import ee
|
| 3 |
+
|
| 4 |
+
|
| 5 |
+
def gba_tile_id(lon: float, lat: float, step_deg: int = 5) -> str:
|
| 6 |
+
lon0 = math.floor(lon / step_deg) * step_deg
|
| 7 |
+
lat0 = math.floor(lat / step_deg) * step_deg
|
| 8 |
+
lon1 = lon0 + step_deg
|
| 9 |
+
lat1 = lat0 + step_deg
|
| 10 |
+
|
| 11 |
+
def fmt_lon(x: int) -> str:
|
| 12 |
+
return ("e" if x >= 0 else "w") + f"{abs(x):03d}"
|
| 13 |
+
|
| 14 |
+
def fmt_lat(x: int) -> str:
|
| 15 |
+
return ("n" if x >= 0 else "s") + f"{abs(x):02d}"
|
| 16 |
+
|
| 17 |
+
return f"{fmt_lon(lon0)}_{fmt_lat(lat1)}_{fmt_lon(lon1)}_{fmt_lat(lat0)}"
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def gba_fc_path(tile_id: str) -> str:
|
| 21 |
+
return f"projects/sat-io/open-datasets/GLOBAL_BUILDING_ATLAS/{tile_id}"
|
| 22 |
+
|
| 23 |
+
|
| 24 |
+
class GlobalBuildingAtlasHeight:
|
| 25 |
+
def __init__(self) -> None:
|
| 26 |
+
self._fc_cache = {}
|
| 27 |
+
|
| 28 |
+
def _get_fc(self, tile_id: str) -> ee.FeatureCollection:
|
| 29 |
+
if tile_id not in self._fc_cache:
|
| 30 |
+
self._fc_cache[tile_id] = ee.FeatureCollection(gba_fc_path(tile_id))
|
| 31 |
+
return self._fc_cache[tile_id]
|
| 32 |
+
|
| 33 |
+
def get_height_m(self, lat: float, lon: float, buffer_m: float = 20.0, centroid_scale_m: float = 1.0):
|
| 34 |
+
tile_id = gba_tile_id(lon, lat)
|
| 35 |
+
fc = self._get_fc(tile_id)
|
| 36 |
+
|
| 37 |
+
pt = ee.Geometry.Point([lon, lat])
|
| 38 |
+
search_geom = pt.buffer(buffer_m) if buffer_m and buffer_m > 0 else pt
|
| 39 |
+
|
| 40 |
+
candidates = (
|
| 41 |
+
fc.filterBounds(search_geom)
|
| 42 |
+
.filter(ee.Filter.gt("height", 0))
|
| 43 |
+
)
|
| 44 |
+
|
| 45 |
+
def add_dist(f):
|
| 46 |
+
f = ee.Feature(f)
|
| 47 |
+
d = f.geometry().centroid(centroid_scale_m).distance(pt)
|
| 48 |
+
return f.set("dist_m", d)
|
| 49 |
+
|
| 50 |
+
best = ee.Feature(candidates.map(add_dist).sort("dist_m").first())
|
| 51 |
+
|
| 52 |
+
count = candidates.size().getInfo()
|
| 53 |
+
if count == 0:
|
| 54 |
+
return {
|
| 55 |
+
"status": "not_found",
|
| 56 |
+
"tile_id": tile_id,
|
| 57 |
+
"buffer_m": buffer_m,
|
| 58 |
+
"predicted_height": None,
|
| 59 |
+
}
|
| 60 |
+
|
| 61 |
+
props = best.toDictionary(["height", "dist_m"]).getInfo()
|
| 62 |
+
|
| 63 |
+
return {
|
| 64 |
+
"status": "success",
|
| 65 |
+
"tile_id": tile_id,
|
| 66 |
+
"buffer_m": buffer_m,
|
| 67 |
+
"predicted_height": props.get("height"),
|
| 68 |
+
"distance_m": props.get("dist_m"),
|
| 69 |
+
}
|