I'm intentionally going in naively, from scratch, and not following a happy path set out by the docs. Jump to bold text for findings or thoughts I think are important.
Installing
First, I attempted to uv tool install globato and got and error:
`uv tool install globato` error
$ uv tool install globato
Resolved 31 packages in 776ms
× Failed to build `fiona==1.10.1`
├─▶ The build backend returned an error
╰─▶ Call to `setuptools.build_meta.build_wheel` failed (exit status: 1)
[stderr]
WARNING:root:Failed to get options via gdal-config: [Errno 2] No such file or directory: 'gdal-config'
CRITICAL:root:A GDAL API version must be specified. Provide a path to gdal-config using a GDAL_CONFIG environment variable or use a GDAL_VERSION environment variable.
hint: This usually indicates a problem with the package or the build environment.
help: `fiona` (v1.10.1) was included because `globato` (v0.2.2) depends on `fiona`
Of course, I need GDAL. I intentionally never install GDAL directly on my system, only in isolated environments. And I think that's an important best practice that we should ensure works for our users.
At this point, I checked the docs, and this is not mentioned.
Let's try installing it from conda-forge with pixi global install globato:
`pixi global install globato` error
No global environments found.
WARN Couldn't install globato
× failed to solve the environment
╰─▶ Cannot solve the request because of: No candidates were found for globato *.
Error: × Some environments couldn't be installed.
Hm, shoot. We should really get this and transformez on conda-forge to support an easy install path :) Happy to help with this!
pixi global install --with gdal --pypi globato should work?
Shit ("shoot" is for 2 errors, "shit" is for 3 😆). Nope. Installing into a global env from PyPI isn't supported yet. I think that's important for this use case of PyPI packages with conda-forge dependencies. I guess I'm doing this the inconvenient way 😆
pixi init
pixi add gdal
pixi add --pypi globato
🎉 Success! Installed.
Using
$ pixi run globato --help
Usage: globato [OPTIONS] COMMAND [ARGS]...
Globato: The ContinUous-DEM Generation Framework.
Options:
--help Show this message and exit.
Commands:
dem Generate custom Digital Elevation Models (Legacy Waffles style).
raster Raster manipulation tools (Powered by Grits).
recipe Execute and manage YAML DEM recipes.
Yay!
recipe list
$ pixi run globato recipe list
Fetching community recipes catalog from GitHub...
📚 Available Community Recipes:
➔ western_ak
💡 Run 'globato recipe info <name>' to see what a recipe does.
Wow, I love the feedback and layout of information here. Knowing where the recipes are coming from is great! Maybe we could be even more specific and put the repo URL.
recipe info
$ pixi run globato recipe info western_ak
Fetching recipe from https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems//western_ak.yaml...
Error resolving recipe 'western_ak': 404 Client Error: Not Found for url: https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/western_ak.yaml
Dangit! Did I make a typo? Let me test with a recipe name that I know is not valid:
$ pixi run globato recipe info western_af
Fetching recipe from https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems//western_af.yaml...
Error resolving recipe 'western_af': 404 Client Error: Not Found for url: https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/western_af.yaml
Same error. I can't tell the difference between a typo and a network error. We need better messaging -- e.g. when using a named recipe, error that the recipe doesn't exist before attempting to fetch it.
I'm going to check out what's up on GitHub now. The URL is wrong, it's missing the /general/ directory.
I would suggest having a manifest in the root of this recipe repo, e.g. cudem-recipes.yml, which is read by globato when finding recipes. It includes the recipe names and links (relative paths within the repo) to the recipe itself. This way, the tool no longer needs to know anything about the repo structure, it's self-describing. And we should have a test that runs in GitHub Actions to verify that the links in this manifest are correct, so we don't move something around and break the manifest without realizing it. In the future, users who follow this standard can create their own recipe repos, and structure them as they like, and point globato at them.
$ pixi run globato recipe info https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/general/western_ak.yaml
Fetching recipe from https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/general/western_ak.yaml...
🏷️ Recipe: Western_AK_Test
Description: This run executes the shared Western Alaska recipe. It gathers elevation and bathymetry inputs for the area, including Copernicus, MBDB, NOAA hydro (BAG/XYZ), chart soundings, and eHydro. Source data is filtered, unzipped, spatially cropped, and reprojected to EPSG:4326+3855. The inputs are combined into a weighted raster stack (test_ak_stack.tif) at approximately 1 arc-second resolution. This stack is blended in two passes to smooth overlaps, and then a CUDEM-style step-down interpolation is applied at 1s / 3s / 9s scales using the coastline as a barrier to generate the final seamless terrain surface (western_alaska_dem.tif).
Region: [-166.0, -162.5, 62.0, 63.5]
Sources: copernicus, ehydro, nos_hydro, mbdb, charts
Yay! Again, I love the information layout. To maybe nitpick, we could bold the field labels, e.g. Description, Region, Sources.
Thinking out loud, but didn't look in to it: Is there a command I can use to learn more about these sources? I recognize some of them, but not all. Maybe we could even prompt the user to let them know this command exists. I like tools which suggest what I might want to do next (git status is a good example).
Nitpick: Maybe the "Fetching" log lines could be greyed (dim in Rich), or only shown with a --debug-log type flag? And perhaps the recipe info could be displayed in a Rich Panel?
At this point I went back to Zulip and see that you showed the command globato recipe info general/western_ak. That works. But it's definitely not clear from the output of these commands. It says the name of the recipe is western_ak and to Run 'globato recipe info <name>' to see what a recipe does., and I did exactly what it said, but it didn't work!
recipe run
I'm going to pretend I didn't learn what I mentioned in the previous paragraph. I want to be doing this naively :)
$ pixi run globato recipe run https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/general/western_ak.yaml
Fetching recipe from https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/general/western_ak.yaml...
Executing recipe: https://raw.githubusercontent.com/continuous-dems/dem-recipes/refs/heads/main/dems/general/western_ak.yaml
Yay! It's happening! I love the progress bars. Thinking out loud: Where is this saving these files?
I also love that this is downloading in parallel. Very nice touch! 🤩
I like the verbosity of the logs, on one hand, but I'm also not sure what some of the INFO messages mean and which are important. I'd suggest moving some log messages to DEBUG level if you can, and adding more context to others.
E.g.:
Initialized Detailed Source Masks in ./ak_debug_masks I'm not sure this is valuable to a typical user, would perhaps downgrade to DEBUG level.
[nos_hydro] Querying remote API... I'm pretty sure I don't need to see this, recommend DEBUG level. Maybe the "Found N items/surveys" messages could stay at INFO level? But that's even debatable :)
Passed 6181462 data points from https://opentopography...._W165_00_DEM.tif?token= -- what do we mean by passed? Passed to what? Why did we select that number of data points? Are there more we didn't select, or is that all of them from this source?
[ INFO ] globato.hooks.filters.rangez: Filtered 2809362 points I love this French theme we have going 😁 What do we mean by "filtered"? Why are we filtering? Are we filtering spatially, temporally, something else? Are we filtering that number of points out, or are we keeping them? "Filter" is a pet peeve word for me, people use it very ambiguously to mean either remove or keep. It's important to balance conciseness and information-richness, so perhaps something like Selected N points matching region of interest (or Dropped N points not in region of interest) is what we should shoot for.
Later.... Oh no! Errors!
[ ERROR ] fetchez.core: Failed to download https://data.ngdc.noaa.gov/platforms/ocean/ships/healy/HLY1902/multibeam/data/version1/MB/em122/0842_20191016_220102_Healy.all.mb58.fbt: Status 404
What does this mean to me the user? Is this catastrophic? As someone familiar with Python log levels, I assume not, because it's not CRITICAL. But many users aren't familiar with Python log levels so some reassurance might be useful. E.g. Trying alternate dataset...
[ ERROR ] fetchez.core: Failed: 0303_20170628_152420_Sikuliaq.all.mb58.fbt
What operation failed here? Is it recoverable? Do I need to keep this in mind?
[ WARNING ] fiona.ogrext: Skipping field FFPT_RIND: invalid type (1, 0)
I think it's fine to ignore this, and since this is coming from Fiona we don't have much control over this, but maybe we can capture this and provide something more user friendly, or hide this unless we're in debug mode. Thinking out loud: "(1, 0)" isn't a "type" as in float32, so what is it? 🤔
[ INFO ] globato.hooks.sinks.raster_writer: Draining raster stream to disk: /tmp/cudem/test_ak_stack_final_blend.tif
Sink/drain are not familiar terms to many users. Is this important for the user to see? If so, let's just say "Writing"?
[ INFO ] fetchez.hooks.audit: Audit log written to audit.json
This jumps out as really important for me to know as a user. If something goes wrong, I probably want to check it. But it's hidden among many messages. Perhaps this should be DEBUG level and we should print that information again at the end so the user knows.
[ WARNING ] globato.hooks.rasters.cudem: PyGMT is missing or failed to load. Falling back to Scipy interpolation.
What's the impact of this? Should I have installed PyGMT to make this go faster?
[ ERROR ] fetchez.core: Request from https://tnmaccess.nationalmap.gov/api/v1/products?bbox=-166.1%2C61.9%2C-162.4%2C63.6&max=100&offset=0&datasets=National+Hydrography+Dataset+%28NHD%29+Best+Resolution&prodExtents=%27HU-8+Subbasin%2CHU-4+Subregion%27 returned 500
[ ERROR ] fetchez.modules.tnm: TNM API Failed: No Response
Again, is this important to me as the end-user? Is it recoverable? Do I need to do anything?
[ INFO ] fetchez.fred: Loaded index copernicus from /tmp/cudem/.pixi/envs/d...data/copernicus.geojson (26911 items)
[ INFO ] fetchez.modules.base: [copernicus] Querying remote API...
I think this could probably be DEBUG, but I love that copernicus, a unique identifier, is highlighted in cyan. Is this the case throughout the logs? I see it's not highlighted on the next line. I think this is the first time I've seen it. I've noticed numbers and URLs are highlighted, but that's default Rich behavior.
Downloading again
Again, love the progress bars. But why are we downloading again? To me, naive user, downloading seems like a thing we do at the beginning. Having a nice Rich Panel view that tells us why we're downloading alongside the progress bars might be really useful here and every other time we enter a downloading phase.
Done downloading!
Whew, that took a little while :) I didn't see any sort of completion message for the downloads; it'd be nice to know what data was downloaded, how long it took, how much disk space was used, but that's less important IMO than just logging that it was completed.
[ INFO ] globato.modules.glob_coast: Voting: 10_DEM_y60x-170.zip as 'copernicus' (Weight: 5.0)
[ INFO ] globato.modules.glob_coast: Voting: Copernicus_DSM_10_N63_00_W163_00_DEM.tif as 'copernicus' (Weight: 5.0)
...etc...
Voting? That's cool? What are we voting for? Who won? I think we can probably hide these individual log messages and log a single message showing a report of the results and what aspects of the process matter to the user (the weights?)
[ INFO ] globato.hooks.rasters.cudem: --- CUDEM STEP 1 | Res: 0.0008333333333333334 | Min Weight: 0.5 ---
[ INFO ] globato.hooks.rasters.fill: [RasterFill] Interpolating/extrapolating voids using Poisson solver (Max Dist: 1000.0)...
[ INFO ] globato.hooks.rasters.cudem: --- CUDEM STEP 0 | Res: 0.0002777777777777778 | Min Weight: 1.0 ---
Step 1? Step 0? This has been running for like 20 minutes at this point, shouldn't those numers be higher?
Oh, it's done! 🎉
Towards the end I see a couple errors.
[ ERROR ] globato.hooks.viz.geohillshade: You must have matplotlib installed; get it with `pip install matplotlib`
Shit. I should have had matplotlib installed the whole time? I wish I knew that at the start. Let's check at the beginning and warn the user of the consequences before the whole process starts?
[ ERROR ] globato.hooks.rasters.base: StreamHook viz_geoshade failed on /tmp/cudem/western_alaska_dem.tif: Source shape (7, 256, 256) is inconsistent with given indexes 3
Does this matter to me? How did it impact the output?
Outputs
Description: This run executes the shared Western Alaska recipe. It gathers elevation an...
If this will be truncated, maybe this isn't useful information at this stage. Perhaps we could consider an id (short name, e.g. Western_AK_Test) and name (long, prose name, e.g. Western Alaska test) and description as 3 separate things. There's a 4th thing, the recipe filename western_ak, and perhaps the id/short name should be the recipe filename? I don't see value in having western_ak and Western_AK_Test separately.
Sources: copernicus (x2), mbdb, nos_hydro (x2), charts, ehydro
What does x2 mean? 2 "granules"/files were used?
💾 OUTPUTS SAVED TO: /tmp/cudem
➔ western_ak_provenance.tif
➔ test_ak_stack.tif
➔ western_alaska_dem.tif
Again, wonderful information layout.
Why are we using alaska sometimes and ak other times? Suggest consistency!
What is test_ak_stack.tif? Suggest labeling these outputs with more context or changing the filenames to be more intuitive. E.g. test_ak_stack.tif: Prose explanation of what this file is for. Maybe bold the filename.
Full processing receipt saved to: western_ak_test_receipt.md
I LOVE this idea, especially that it's markdown and human-readable. Let's display the files that were generated, and maybe their checksums?
There are so many files in my current directory. I didn't realize all the intermediate data would be saved here -- I almost ran this in my home dir, and that would have been no fun. Perhaps we should (a) confirm for the user where the data will be written, and/or (b) write some of this data ephemerally to /tmp, and/or (c) organize more of this data into subdirs (the files starting with temp_ seem like they shouldn't be in the current directory).
Results
Viewing
I was able to open western_alaska_dem.tif in QGIS, yay!
Looks to be geolocated correctly.
Noticed the viewer was slow. Was unable to load in JupyterGIS, likely because of lack of optimization? Unsure ATM :)
Inspecting
From gdal info, it seems western_alaska_dem.tif doesn't have overviews. Recommend making these as Cloud-Optimized GeoTIFFs (COGs) to optimize both local and remote performance!
Audit
Suggest including versions of all relevant tools in here.
I'm intentionally going in naively, from scratch, and not following a happy path set out by the docs. Jump to bold text for findings or thoughts I think are important.
Installing
First, I attempted to
uv tool install globatoand got and error:`uv tool install globato` error
Of course, I need GDAL. I intentionally never install GDAL directly on my system, only in isolated environments. And I think that's an important best practice that we should ensure works for our users.
At this point, I checked the docs, and this is not mentioned.
Let's try installing it from
conda-forgewithpixi global install globato:`pixi global install globato` error
Hm, shoot. We should really get this and transformez on conda-forge to support an easy install path :) Happy to help with this!
pixi global install --with gdal --pypi globatoshould work?Shit ("shoot" is for 2 errors, "shit" is for 3 😆). Nope. Installing into a global env from PyPI isn't supported yet. I think that's important for this use case of PyPI packages with conda-forge dependencies. I guess I'm doing this the inconvenient way 😆
🎉 Success! Installed.
Using
Yay!
recipe list$ pixi run globato recipe list Fetching community recipes catalog from GitHub... 📚 Available Community Recipes: ➔ western_ak 💡 Run 'globato recipe info <name>' to see what a recipe does.Wow, I love the feedback and layout of information here. Knowing where the recipes are coming from is great! Maybe we could be even more specific and put the repo URL.
recipe infoDangit! Did I make a typo? Let me test with a recipe name that I know is not valid:
Same error. I can't tell the difference between a typo and a network error. We need better messaging -- e.g. when using a named recipe, error that the recipe doesn't exist before attempting to fetch it.
I'm going to check out what's up on GitHub now. The URL is wrong, it's missing the
/general/directory.I would suggest having a manifest in the root of this recipe repo, e.g.
cudem-recipes.yml, which is read byglobatowhen finding recipes. It includes the recipe names and links (relative paths within the repo) to the recipe itself. This way, the tool no longer needs to know anything about the repo structure, it's self-describing. And we should have a test that runs in GitHub Actions to verify that the links in this manifest are correct, so we don't move something around and break the manifest without realizing it. In the future, users who follow this standard can create their own recipe repos, and structure them as they like, and pointglobatoat them.Yay! Again, I love the information layout. To maybe nitpick, we could bold the field labels, e.g. Description, Region, Sources.
Thinking out loud, but didn't look in to it: Is there a command I can use to learn more about these sources? I recognize some of them, but not all. Maybe we could even prompt the user to let them know this command exists. I like tools which suggest what I might want to do next (
git statusis a good example).Nitpick: Maybe the "Fetching" log lines could be greyed (
dimin Rich), or only shown with a--debug-logtype flag? And perhaps the recipe info could be displayed in a RichPanel?At this point I went back to Zulip and see that you showed the command
globato recipe info general/western_ak. That works. But it's definitely not clear from the output of these commands. It says the name of the recipe iswestern_akand toRun 'globato recipe info <name>' to see what a recipe does., and I did exactly what it said, but it didn't work!recipe runI'm going to pretend I didn't learn what I mentioned in the previous paragraph. I want to be doing this naively :)
Yay! It's happening! I love the progress bars. Thinking out loud: Where is this saving these files?
I also love that this is downloading in parallel. Very nice touch! 🤩
I like the verbosity of the logs, on one hand, but I'm also not sure what some of the INFO messages mean and which are important. I'd suggest moving some log messages to DEBUG level if you can, and adding more context to others.
E.g.:
Initialized Detailed Source Masks in ./ak_debug_masksI'm not sure this is valuable to a typical user, would perhaps downgrade to DEBUG level.[nos_hydro] Querying remote API...I'm pretty sure I don't need to see this, recommend DEBUG level. Maybe the "Found N items/surveys" messages could stay at INFO level? But that's even debatable :)Passed 6181462 data points from https://opentopography...._W165_00_DEM.tif?token=-- what do we mean by passed? Passed to what? Why did we select that number of data points? Are there more we didn't select, or is that all of them from this source?[ INFO ] globato.hooks.filters.rangez: Filtered 2809362 pointsI love this French theme we have going 😁 What do we mean by "filtered"? Why are we filtering? Are we filtering spatially, temporally, something else? Are we filtering that number of points out, or are we keeping them? "Filter" is a pet peeve word for me, people use it very ambiguously to mean either remove or keep. It's important to balance conciseness and information-richness, so perhaps something likeSelected N points matching region of interest(orDropped N points not in region of interest) is what we should shoot for.Later.... Oh no! Errors!
What does this mean to me the user? Is this catastrophic? As someone familiar with Python log levels, I assume not, because it's not CRITICAL. But many users aren't familiar with Python log levels so some reassurance might be useful. E.g.
Trying alternate dataset...What operation failed here? Is it recoverable? Do I need to keep this in mind?
I think it's fine to ignore this, and since this is coming from Fiona we don't have much control over this, but maybe we can capture this and provide something more user friendly, or hide this unless we're in debug mode. Thinking out loud: "(1, 0)" isn't a "type" as in
float32, so what is it? 🤔Sink/drain are not familiar terms to many users. Is this important for the user to see? If so, let's just say "Writing"?
This jumps out as really important for me to know as a user. If something goes wrong, I probably want to check it. But it's hidden among many messages. Perhaps this should be DEBUG level and we should print that information again at the end so the user knows.
What's the impact of this? Should I have installed PyGMT to make this go faster?
Again, is this important to me as the end-user? Is it recoverable? Do I need to do anything?
I think this could probably be DEBUG, but I love that
copernicus, a unique identifier, is highlighted in cyan. Is this the case throughout the logs? I see it's not highlighted on the next line. I think this is the first time I've seen it. I've noticed numbers and URLs are highlighted, but that's default Rich behavior.Downloading again
Again, love the progress bars. But why are we downloading again? To me, naive user, downloading seems like a thing we do at the beginning. Having a nice Rich Panel view that tells us why we're downloading alongside the progress bars might be really useful here and every other time we enter a downloading phase.
Done downloading!
Whew, that took a little while :) I didn't see any sort of completion message for the downloads; it'd be nice to know what data was downloaded, how long it took, how much disk space was used, but that's less important IMO than just logging that it was completed.
Voting? That's cool? What are we voting for? Who won? I think we can probably hide these individual log messages and log a single message showing a report of the results and what aspects of the process matter to the user (the weights?)
Step 1? Step 0? This has been running for like 20 minutes at this point, shouldn't those numers be higher?
Oh, it's done! 🎉
Towards the end I see a couple errors.
Shit. I should have had matplotlib installed the whole time? I wish I knew that at the start. Let's check at the beginning and warn the user of the consequences before the whole process starts?
Does this matter to me? How did it impact the output?
Outputs
If this will be truncated, maybe this isn't useful information at this stage. Perhaps we could consider an
id(short name, e.g.Western_AK_Test) andname(long, prose name, e.g.Western Alaska test) anddescriptionas 3 separate things. There's a 4th thing, the recipe filenamewestern_ak, and perhaps the id/short name should be the recipe filename? I don't see value in havingwestern_akandWestern_AK_Testseparately.What does x2 mean? 2 "granules"/files were used?
Again, wonderful information layout.
Why are we using
alaskasometimes andakother times? Suggest consistency!What is
test_ak_stack.tif? Suggest labeling these outputs with more context or changing the filenames to be more intuitive. E.g.test_ak_stack.tif: Prose explanation of what this file is for.Maybe bold the filename.I LOVE this idea, especially that it's markdown and human-readable. Let's display the files that were generated, and maybe their checksums?
There are so many files in my current directory. I didn't realize all the intermediate data would be saved here -- I almost ran this in my home dir, and that would have been no fun. Perhaps we should (a) confirm for the user where the data will be written, and/or (b) write some of this data ephemerally to
/tmp, and/or (c) organize more of this data into subdirs (the files starting withtemp_seem like they shouldn't be in the current directory).Results
Viewing
I was able to open
western_alaska_dem.tifin QGIS, yay!Looks to be geolocated correctly.
Noticed the viewer was slow. Was unable to load in JupyterGIS, likely because of lack of optimization? Unsure ATM :)
Inspecting
From
gdal info, it seemswestern_alaska_dem.tifdoesn't have overviews. Recommend making these as Cloud-Optimized GeoTIFFs (COGs) to optimize both local and remote performance!Audit
Suggest including versions of all relevant tools in here.