From 32113bcf9e9ae5bdd28edea728b331513174afab Mon Sep 17 00:00:00 2001 From: Julia Signell Date: Wed, 15 Mar 2023 16:36:15 -0400 Subject: [PATCH] feat: include fields-normalized.json in package --- CHANGELOG.md | 8 +++++ RELEASING.md | 19 ++++++----- pystac/static/__init__.py | 0 pystac/static/fields-normalized.json | 1 + pystac/summaries.py | 51 ++++++++++++++++++---------- scripts/pull-static | 11 ++++++ setup.py | 5 ++- tests/test_summaries.py | 18 +++++----- 8 files changed, 76 insertions(+), 37 deletions(-) create mode 100644 pystac/static/__init__.py create mode 100644 pystac/static/fields-normalized.json create mode 100755 scripts/pull-static diff --git a/CHANGELOG.md b/CHANGELOG.md index e200a86b6..16b6ce1cf 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,14 @@ ## [Unreleased] +### Changed + +- Include a copy of the `fields.json` file (for summaries) with each distribution of PySTAC ([#1045](https://github.com/stac-utils/pystac/pull/1045)) + +### Deprecated + +- `pystac.summaries.FIELDS_JSON_URL` ([#1045](https://github.com/stac-utils/pystac/pull/1045)) + ### [v1.7.1] ### Changed diff --git a/RELEASING.md b/RELEASING.md index c3d14ec4a..d150c9129 100644 --- a/RELEASING.md +++ b/RELEASING.md @@ -4,27 +4,28 @@ This is a checklist to use when releasing a new PySTAC version. 1. Determine the next version. We do not currently have a versioning guide, but has some discussion around the topic. 2. Create a release branch with the name `release/vX.Y.Z`, where `X.Y.Z` is the next version (e.g. `1.7.0`). -3. Update the `__version__` attribute in `pystac/version.py` with the new version. -4. Update the CHANGELOG. +3. Pull fields-normalized.json from cdn: run `scripts/pull-static`. Note you will need to have [jq](https://stedolan.github.io/jq/) installed. +4. Update the `__version__` attribute in `pystac/version.py` with the new version. +5. Update the CHANGELOG. - Create a new header below `## [Unreleased]` with the new version. - Remove any unused header sections. - Update the links at the bottom of the page for the new header. - Audit the CHANGELOG for correctness and readability. -5. Audit the changes. +6. Audit the changes. Use the CHANGELOG, your favorite diff tool, and the merged Github pull requests to ensure that: - All notable changes are captured in the CHANGELOG. - The type of release is appropriate for the new version number, i.e. if there are breaking changes, the MAJOR version number must be increased. - All deprecated items that were marked for removal in this version are removed. -6. Craft draft release notes (). +7. Craft draft release notes (). These should be short, readable, and call out any significant changes, especially changes in default behavior or significant new features. These should also include a link back to the Github milestone for this release, if there is one. These should _not_ be a complete listing of changes -- those will be auto-generated later, after the tag is pushed. -7. Commit your changes, push your branch to Github, and request a review. -8. Once approved, merge the PR. -9. Once the PR is merged, create a tag with the version name, e.g. `vX.Y.Z`. +8. Commit your changes, push your branch to Github, and request a review. +9. Once approved, merge the PR. +10. Once the PR is merged, create a tag with the version name, e.g. `vX.Y.Z`. Prefer a signed tag, if possible. Push the tag to Github. -10. Use the tag to finish your release notes, and publish those. +11. Use the tag to finish your release notes, and publish those. The "auto generate" feature is your friend, here. When the release is published, this will trigger the build and release on PyPI. -11. Announced the release in [Gitter](https://matrix.to/#/#SpatioTemporal-Asset-Catalog_python:gitter.im) and on any relevant social media. +12. Announced the release in [Gitter](https://matrix.to/#/#SpatioTemporal-Asset-Catalog_python:gitter.im) and on any relevant social media. diff --git a/pystac/static/__init__.py b/pystac/static/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/pystac/static/fields-normalized.json b/pystac/static/fields-normalized.json new file mode 100644 index 000000000..9e7b2af36 --- /dev/null +++ b/pystac/static/fields-normalized.json @@ -0,0 +1 @@ +{"metadata":{"id":{"label":"Identifier"},"keywords":{"label":"Keywords"},"datetime":{"label":"Time of Data","format":"Timestamp","summary":false},"title":{"label":"Title","summary":false},"description":{"label":"Description","format":"CommonMark","summary":false},"start_datetime":{"label":"Time of Data begins","format":"Timestamp","summary":false},"end_datetime":{"label":"Time of Data ends","format":"Timestamp","summary":false},"created":{"label":"Created","format":"Timestamp","summary":"r"},"updated":{"label":"Updated","format":"Timestamp","summary":"r"},"published":{"label":"Published","format":"Timestamp","summary":"r"},"expires":{"label":"Expires","format":"Timestamp","summary":"r"},"unpublished":{"label":"Unpublished","format":"Timestamp","summary":"r"},"license":{"label":"License","format":"License","summary":false},"providers":{"label":"Providers","format":"Providers","summary":false},"platform":{"label":"Platform"},"instruments":{"label":"Instruments","format":"CSV"},"constellation":{"label":"Constellation"},"mission":{"label":"Mission"},"gsd":{"label":"GSD","explain":"Ground Sample Distance","unit":"m"},"version":{"label":"Data Version","summary":false},"deprecated":{"label":"Deprecated","summary":false},"language":{"label":"Current Language","ext":"language","summary":"v","properties":{"name":{"label":"Name"},"alternate":{"label":"Alternate Name"},"code":{"label":"Code"},"dir":{"label":"Direction","explain":"Reading and writing direction","mapping":{"ltr":"left-to-right","rtl":"right-to-left"},"default":"ltr"}}},"languages":{"label":"Available Languages","ext":"language","summary":false,"items":{"name":{"label":"Name","sortable":true,"order":0},"alternate":{"label":"Alternate Name","sortable":true,"order":1},"code":{"label":"Code","sortable":true,"order":2},"dir":{"label":"Direction","explain":"Reading and writing direction","sortable":true,"order":3,"mapping":{"ltr":"left-to-right","rtl":"right-to-left"},"default":"ltr"}},"itemOrder":["name","alternate","code","dir"]},"crs":{"label":"CRS","format":"CRS","explain":"Coordinate Reference System"},"anon:size":{"label":"Uncertainty","unit":"°","explain":"The size of one side of the anonymized bounding box"},"anon:warning":{"label":"Warning","summary":false},"classification:classes":{"summary":false,"label":"Classes","items":{"color_hint":{"label":"Color","order":0,"format":"HexColor"},"value":{"label":"Value","order":1},"title":{"label":"Title","order":2},"name":{"label":"Identifier","order":3},"description":{"label":"Description","order":4,"format":"CommonMark"},"nodata":{"label":"No-data value","order":5,"default":false}},"itemOrder":["color_hint","value","title","name","description","nodata"]},"classification:bitfields":{"summary":false,"label":"Bit Mask","items":{"name":{"label":"Name","order":0},"offset":{"label":"Offset","explain":"Offset to the first bit","order":1},"length":{"label":"Number of bits","order":2},"description":{"label":"Description","order":3,"format":"CommonMark"},"classes":{"alias":"classification:classes","summary":false,"label":"Classes","items":{"color_hint":{"label":"Color","order":0,"format":"HexColor"},"value":{"label":"Value","order":1},"title":{"label":"Title","order":2},"name":{"label":"Identifier","order":3},"description":{"label":"Description","order":4,"format":"CommonMark"},"nodata":{"label":"No-data value","order":5,"default":false}},"itemOrder":["color_hint","value","title","name","description","nodata"]},"roles":{"label":"Purpose"}},"itemOrder":["classes","name","offset","length","description","roles"]},"cube:dimensions":{"label":"Dimensions","summary":false,"listWithKeys":true,"items":{"type":{"label":"Type","order":0},"axis":{"label":"Axis","order":1},"description":{"label":"Description","format":"CommonMark","order":2},"extent":{"label":"Extent","format":"Extent","order":3},"values":{"label":"Values","order":4},"step":{"label":"Step","order":5},"unit":{"alias":"file:unit","order":5,"label":"Unit of Values"},"reference_system":{"label":"Reference System","explain":"Coordinate / Temporal / Other Reference System","order":6}},"itemOrder":["type","axis","description","extent","values","step","unit","reference_system"]},"cube:variables":{"label":"Variables","summary":false,"listWithKeys":true,"items":{"dimensions":{"label":"Dimensions","order":0},"type":{"label":"Type","order":1,"mapping":{"data":"Measured values","auxiliary":"Coordinate data"}},"description":{"label":"Description","format":"CommonMark","order":2},"extent":{"label":"Extent","format":"Extent","order":3},"values":{"label":"Values","order":4},"step":{"label":"Step","order":5},"unit":{"alias":"file:unit","order":6,"label":"Unit of Values"}},"itemOrder":["dimensions","type","description","extent","values","step","unit"]},"eo:bands":{"label":"Spectral Bands","items":{"name":{"label":"Name","sortable":true,"id":true,"order":0},"common_name":{"label":"Common Name","sortable":true,"order":1},"description":{"label":"Description","format":"CommonMark","order":2},"center_wavelength":{"label":"Wavelength","explain":"The center wavelength of the band","unit":"μm","sortable":true,"order":5},"full_width_half_max":{"label":"FWHM","explain":"Full Width Half Max","unit":"μm","sortable":true,"order":6},"gsd":{"alias":"gsd","sortable":true,"order":3,"label":"GSD","explain":"Ground Sample Distance","unit":"m"},"cloud_cover":{"alias":"eo:cloud_cover","sortable":true,"order":4,"label":"Cloud Cover","unit":"%"},"solar_illumination":{"label":"Solar Illumination","sortable":true,"order":7,"unit":"W/m²/μm"},"classification:classes":{"alias":"classification:classes","summary":false,"label":"Classes","items":{"color_hint":{"label":"Color","order":0,"format":"HexColor"},"value":{"label":"Value","order":1},"title":{"label":"Title","order":2},"name":{"label":"Identifier","order":3},"description":{"label":"Description","order":4,"format":"CommonMark"},"nodata":{"label":"No-data value","order":5,"default":false}},"itemOrder":["color_hint","value","title","name","description","nodata"]},"classification:bitfields":{"alias":"classification:bitfields","summary":false,"label":"Bit Mask","items":{"name":{"label":"Name","order":0},"offset":{"label":"Offset","explain":"Offset to the first bit","order":1},"length":{"label":"Number of bits","order":2},"description":{"label":"Description","order":3,"format":"CommonMark"},"classes":{"alias":"classification:classes","summary":false,"label":"Classes","items":{"color_hint":{"label":"Color","order":0,"format":"HexColor"},"value":{"label":"Value","order":1},"title":{"label":"Title","order":2},"name":{"label":"Identifier","order":3},"description":{"label":"Description","order":4,"format":"CommonMark"},"nodata":{"label":"No-data value","order":5,"default":false}},"itemOrder":["color_hint","value","title","name","description","nodata"]},"roles":{"label":"Purpose"}},"itemOrder":["classes","name","offset","length","description","roles"]}},"itemOrder":["name","classification:bitfields","classification:classes","common_name","description","gsd","cloud_cover","center_wavelength","full_width_half_max","solar_illumination"]},"eo:cloud_cover":{"label":"Cloud Cover","unit":"%"},"eo:snow_cover":{"label":"Snow/Ice Cover","unit":"%"},"forecast:reference_datetime":{"label":"Reference Time","format":"Timestamp","summary":"r"},"forecast:horizon":{"label":"Forecast Horizon","explain":"The time between the reference time and the forecast time","format":"Duration","summary":"r"},"forecast:duration":{"label":"Forecast Length","format":"Duration","summary":"r"},"file:bits_per_sample":{"label":"Bits per Sample"},"file:byte_order":{"label":"Byte Order"},"file:checksum":{"label":"Checksum","format":"Checksum","summary":false},"file:data_type":{"label":"Data Type of Values","format":"FileDataType"},"file:header_size":{"label":"Header Size","format":"FileSize","summary":false},"file:nodata":{"label":"No-Data Values","format":"CSV","summary":false},"file:size":{"label":"Size","format":"FileSize","summary":false},"file:unit":{"label":"Unit of Values"},"file:values":{"label":"Map of Values","summary":false,"items":{"values":{"label":"Values","format":"CSV","order":1},"summary":{"label":"Summary","order":0}},"itemOrder":["summary","values"]},"file:local_path":{"label":"Local Path","summary":false},"goes:orbital_slot":{"label":"Orbital Slot"},"goes:system_environment":{"label":"System Environment","mapping":{"OR":"Operational system, real-time data","OT":"Operational system, test data","IR":"Test system, real-time data","IT":"Test system, test data","IP":"Test system, playback data","IS":"Test system, simulated data"}},"goes:image_type":{"label":"Area","mapping":{"FULL DISK":"The Americas (full disk)","CONUS":"North America (continental US)","MESOSCALE":"Central/South America (mesoscale)"}},"goes:mesoscale_image_number":{"label":"Area in Central/South America","mapping":{"1":"Region 1","2":"Region 2"}},"goes:mode":{"label":"Capture Mode","mapping":{"3":"3: 1x full disk, 3x continental US, 30x mesoscale region 1, 30x mesoscale region 2 (every 15 minutes)","4":"4: 1x full disk (every 5 minutes)","6":"6: 1x full disk, 2x continental US, 20x mesoscale region 1, 20x mesoscale region 2 (every 10 minutes)"}},"goes:group_time_threshold":{"label":"Time Threshold in a Group","explain":"Lightning group maximum time difference among lightning events in a group","unit":"s"},"goes:flash_time_threshold":{"label":"Time Threshold in a Flash","explain":"Lightning flash maximum time difference among lightning events in a flash","unit":"s"},"goes:lightning_wavelength":{"label":"Central Wavelength","unit":"nm"},"goes:yaw_flip_flag":{"label":"Yaw Flip Configuration","explain":"Flag indicating that the spacecraft is operating in yaw flip configuration.","mapping":{"0":"Upright","1":"Neither","2":"Inverted"}},"goes:event_count":{"label":"Lightning Events"},"goes:group_count":{"label":"Lightning Groups"},"goes:flash_count":{"label":"Lightning Flashes"},"goes:nominal_satellite_subpoint_lat":{"label":"Satellite Subpoint Latitude","unit":"°N"},"goes:nominal_satellite_subpoint_lon":{"label":"Satellite Subpoint Longitude","unit":"°E"},"goes:nominal_satellite_height":{"label":"Satellite Height","explain":"Nominal satellite height above GRS 80 ellipsoid","unit":"km"},"goes:percent_navigated_L1b_events":{"label":"Events navigated by Instrument","format":"Percent0to1","unit":"%"},"goes:percent_uncorrectable_L0_errors":{"label":"Data Lost","format":"Percent0to1","unit":"%"},"grid:code":{"label":"Grid","format":"GridCode"},"raster:bands":{"label":"Bands","items":{"nodata":{"alias":"file:nodata","label":"No-Data Values","format":"CSV","summary":false},"sampling":{"label":"Sampling","mapping":{"area":"Area","point":"Point (at pixel center)"}},"data_type":{"alias":"file:data_type","label":"Data Type of Values","format":"FileDataType"},"bits_per_sample":{"alias":"file:bits_per_sample","label":"Bits per Sample"},"spatial_resolution":{"label":"Resolution","explain":"Average spatial resolution","unit":"m"},"statistics":{"label":"Statistics","items":{"mean":{"label":"Average"},"maximum":{"label":"Max.","explain":"Maxmimum value"},"minimum":{"label":"Min.","explain":"Minimum value"},"stdev":{"label":"Std. Dev.","explain":"Standard Deviation"},"valid_percent":{"label":"Valid","explain":"Percentage of valid pixels","unit":"%"}},"itemOrder":["mean","maximum","minimum","stdev","valid_percent"]},"unit":{"alias":"file:unit","label":"Unit of Values"},"scale":{"label":"Scale"},"offset":{"label":"Offset"},"histogram":{"label":"Histogram","custom":true},"classification:classes":{"alias":"classification:classes","summary":false,"label":"Classes","items":{"color_hint":{"label":"Color","order":0,"format":"HexColor"},"value":{"label":"Value","order":1},"title":{"label":"Title","order":2},"name":{"label":"Identifier","order":3},"description":{"label":"Description","order":4,"format":"CommonMark"},"nodata":{"label":"No-data value","order":5,"default":false}},"itemOrder":["color_hint","value","title","name","description","nodata"]},"classification:bitfields":{"alias":"classification:bitfields","summary":false,"label":"Bit Mask","items":{"name":{"label":"Name","order":0},"offset":{"label":"Offset","explain":"Offset to the first bit","order":1},"length":{"label":"Number of bits","order":2},"description":{"label":"Description","order":3,"format":"CommonMark"},"classes":{"alias":"classification:classes","summary":false,"label":"Classes","items":{"color_hint":{"label":"Color","order":0,"format":"HexColor"},"value":{"label":"Value","order":1},"title":{"label":"Title","order":2},"name":{"label":"Identifier","order":3},"description":{"label":"Description","order":4,"format":"CommonMark"},"nodata":{"label":"No-data value","order":5,"default":false}},"itemOrder":["color_hint","value","title","name","description","nodata"]},"roles":{"label":"Purpose"}},"itemOrder":["classes","name","offset","length","description","roles"]}},"itemOrder":["classification:bitfields","bits_per_sample","classification:classes","data_type","histogram","nodata","offset","spatial_resolution","sampling","scale","statistics","unit"]},"label:properties":{"label":"Properties","null":"raster data"},"label:classes":{"label":"Classes","items":{"name":{"label":"Name","null":"raster-formatted","sortable":true,"id":true},"classes":{"label":"Classes"}},"itemOrder":["name","classes"]},"label:description":{"label":"Description","format":"CommonMark","summary":false},"label:type":{"label":"Type"},"label:tasks":{"label":"Tasks"},"label:methods":{"label":"Methods"},"label:overviews":{"label":"Overviews","summary":false,"items":{"property_key":{"label":"Property Key","id":true},"counts":{"label":"Counts","custom":true},"statistics":{"label":"Statistics","custom":true}},"itemOrder":["property_key","counts","statistics"]},"mgrs:latitude_band":{"label":"Latitude Band"},"mgrs:grid_square":{"label":"Grid Square"},"mgrs:utm_zone":{"label":"UTM Zone"},"noaa_mrms_qpe:pass":{"label":"Pass Number","mapping":{"1":"1 (less latency / less gauges)","2":"2 (more latency / more gauges)"}},"noaa_mrms_qpe:period":{"label":"Accumulation Period","unit":"h"},"noaa_mrms_qpe:region":{"label":"Region","mapping":{"CONUS":"Continental US","HAWAII":"Hawaii","GUAM":"Guam","ALASKA":"Alaska","CARIB":"Caribbean Islands"}},"openeo:status":{"label":"Processing Status"},"api_version":{"label":"API Version","ext":"openeo"},"backend_version":{"label":"Back-End Version","ext":"openeo"},"production":{"label":"Production-Ready","ext":"openeo"},"endpoints":{"label":"Supported Endpoints","ext":"openeo","summary":false,"items":{"path":{"label":"Path Template","order":0},"methods":{"label":"HTTP Methods","order":1,"format":"CSV"}},"itemOrder":["path","methods"]},"billing":{"label":"Billing","ext":"openeo","custom":true,"summary":false},"order:status":{"label":"Status","mapping":{"orderable":"Orderable (data can be ordered)","ordered":"Ordered (preparing to deliver data)","pending":"Pending (waiting for activation)","shipping":"Shipping (data is getting processed)","succeeded":"Delivered (data is available)","failed":"Failed (unable to deliver)","canceled":"Canceled (delivery stopped)"}},"order:id":{"label":"Identifier"},"order:date":{"label":"Submitted","format":"Timestamp","summary":"r"},"order:expiration_date":{"alias":"expires","label":"Expires","format":"Timestamp","summary":"r"},"pc:count":{"label":"Points","explain":"Number of Points"},"pc:type":{"label":"Type"},"pc:encoding":{"label":"Format"},"pc:schemas":{"label":"Schemas","summary":false,"items":{"name":{"label":"Name","sortable":true,"id":true},"size":{"label":"Size","unit":"bytes","sortable":true},"type":{"label":"Type","sortable":true}},"itemOrder":["name","size","type"]},"pc:density":{"label":"Density"},"pc:statistics":{"label":"Statistics","summary":"s","items":{"name":{"label":"Name","id":true},"position":{"label":"Position"},"average":{"label":"Average"},"count":{"label":"Count"},"maximum":{"label":"Max.","explain":"Maxmimum value"},"minimum":{"label":"Min.","explain":"Minimum value"},"stddev":{"label":"Std. Dev.","explain":"Standard Deviation"},"variance":{"label":"Variance"}},"itemOrder":["name","average","count","maximum","minimum","position","stddev","variance"]},"processing:expression":{"label":"Processing Instructions","summary":false},"processing:lineage":{"label":"Lineage","format":"CommonMark","summary":false},"processing:level":{"label":"Level"},"processing:facility":{"label":"Facility"},"processing:software":{"label":"Software","format":"Software","summary":false},"proj:epsg":{"label":"EPSG Code","format":"EPSG","summary":"v"},"proj:wkt2":{"label":"WKT2","explain":"Well-Known Text, version 2","format":"WKT2","summary":false},"proj:projjson":{"label":"PROJJSON","explain":"JSON encoding of WKT2","format":"PROJJSON","summary":false},"proj:geometry":{"label":"Footprint","custom":true,"summary":false},"proj:bbox":{"label":"Bounding Box","custom":true,"summary":false},"proj:centroid":{"label":"Centroid","custom":true,"summary":false},"proj:shape":{"label":"Image Dimensions","format":"Shape","summary":false},"proj:transform":{"label":"Transformation Matrix","format":"Transform","summary":false},"sar:instrument_mode":{"label":"Instrument Mode"},"sar:frequency_band":{"label":"Frequency Band"},"sar:center_frequency":{"label":"Center Frequency","unit":"GHz"},"sar:polarizations":{"label":"Polarizations","format":"CSV"},"sar:product_type":{"label":"Product Type"},"sar:resolution_range":{"label":"Range Resolution","unit":"m"},"sar:resolution_azimuth":{"label":"Azimuth Resolution","unit":"m"},"sar:pixel_spacing_range":{"label":"Range Pixel Spacing","unit":"m"},"sar:pixel_spacing_azimuth":{"label":"Aziumth Pixel Spacing","unit":"m"},"sar:looks_range":{"label":"Range Looks"},"sar:looks_azimuth":{"label":"Azimuth Looks"},"sar:looks_equivalent_number":{"label":"ENL","explain":"Equivalent Number of Looks"},"sar:observation_direction":{"label":"Observation Direction"},"sat:platform_international_designator":{"label":"Int. Designator","explain":"International designator for the platform, also known as COSPAR ID and NSSDCA ID."},"sat:orbit_state":{"label":"Orbit State"},"sat:absolute_orbit":{"label":"Abs. Orbit Number","explain":"Absolute Orbit Number"},"sat:relative_orbit":{"label":"Rel. Orbit Number","explain":"Relative Orbit Number"},"sat:anx_datetime":{"label":"ANX Time","explain":"Ascending Node Crossing time","summary":"r"},"sci:doi":{"label":"DOI","format":"DOI"},"sci:citation":{"label":"Citation"},"sci:publications":{"label":"Publications","summary":false,"items":{"citation":{"label":"Publication","sortable":true,"order":0},"doi":{"label":"DOI","format":"DOI","sortable":true,"order":1}},"itemOrder":["citation","doi"]},"ssys:targets":{"label":"Target Body"},"storage:platform":{"label":"Provider","mapping":{"ALIBABA":"Alibaba Cloud","AWS":"Amazon AWS","AZURE":"Microsoft Azure","GCP":"Google Cloud Platform","IBM":"IBM Cloud","ORACLE":"Oracle Cloud"}},"storage:region":{"label":"Region"},"storage:requester_pays":{"label":"Requester Pays"},"storage:tier":{"label":"Tier Type"},"table:columns":{"label":"Columns","items":{"name":{"label":"Name","sortable":true,"id":true,"order":0},"type":{"label":"Data Type","sortable":true,"order":1},"description":{"label":"Description","format":"CommonMark","order":2}},"itemOrder":["name","type","description"]},"table:primary_geometry":{"label":"Primary Geometry Column"},"table:row_count":{"label":"Rows"},"table:tables":{"label":"Tables","summary":false,"listWithKeys":true,"items":{"name":{"label":"Name","sortable":true,"id":true,"order":0},"description":{"label":"Description","format":"CommonMark","order":1}},"itemOrder":["name","description"]},"tiles:tile_matrix_sets":{"label":"Tile Matrix Sets","custom":true,"summary":false},"tiles:tile_matrix_set_links":{"label":"Tile Matrix Set Links","custom":true,"summary":false},"view:off_nadir":{"label":"Off-Nadir Angle","unit":"°"},"view:incidence_angle":{"label":"Incidence Angle","unit":"°"},"view:azimuth":{"label":"Viewing Azimuth","unit":"°"},"view:sun_azimuth":{"label":"Sun Azimuth","unit":"°"},"view:sun_elevation":{"label":"Sun Elevation","unit":"°"},"pl:black_fill":{"label":"Unfilled Image Parts","unit":"%"},"pl:clear_percent":{"label":"Clear Sky","unit":"%"},"pl:grid_cell":{"label":"Grid Cell"},"pl:ground_control":{"label":"Positional Accuracy"},"pl:ground_control_ratio":{"label":"Successful Rectification Ratio"},"pl:item_type":{"label":"Type"},"pl:pixel_resolution":{"label":"Spatial Resolution","unit":"m"},"pl:publishing_stage":{"label":"Publishing Stage","mapping":{"preview":"Preview","standard":"Standard","finalized":"Finalized"}},"pl:quality_category":{"label":"Quality Category","mapping":{"standard":"Standard","test":"Test"}},"pl:strip_id":{"label":"Image Strip ID"},"gee:type":{"label":"Type","mapping":{"image":"Single image","image_collection":"Image collection","table":"Table"}},"gee:cadence":{"label":"Cadence"},"gee:schema":{"label":"Variables","items":{"name":{"label":"Name"},"description":{"label":"Description"},"type":{"label":"Data Type"}},"summary":false,"itemOrder":["type","description","name"]},"gee:revisit_interval":{"label":"Revisit Interval"},"gee:terms_of_use":{"label":"Terms of Use","format":"CommonMark","summary":false},"gee:visualizations":{"label":"Visualizations","custom":true,"summary":false},"landsat:scene_id":{"label":"Scene ID"},"landsat:collection_category":{"label":"Collection Category"},"landsat:collection_number":{"label":"Collection Number"},"landsat:wrs_type":{"label":"WRS Type","explain":"Worldwide Reference System Type"},"landsat:wrs_path":{"label":"WRS Path","explain":"Worldwide Reference System Path"},"landsat:wrs_row":{"label":"WRS Row","explain":"Worldwide Reference System Row"},"landsat:cloud_cover_land":{"label":"Land Cloud Cover","unit":"%"},"msft:container":{"label":"Container"},"msft:storage_account":{"label":"Storage Account"},"msft:short_description":{"label":"Summary","summary":false},"sentinel:utm_zone":{"label":"UTM Zone"},"sentinel:latitude_band":{"label":"Latitude Band"},"sentinel:grid_square":{"label":"Grid Square"},"sentinel:sequence":{"label":"Sequence"},"sentinel:product_id":{"label":"Product ID","summary":"s"},"sentinel:data_coverage":{"label":"Data Coverage","unit":"%"},"sentinel:valid_cloud_cover":{"label":"Valid Cloud Cover"},"cbers:data_type":{"label":"Processing Level","explain":"Geolocation precision level","mapping":{"L2":"Geolocation using only satellite telemetry","L3":"Control points used to geolocate image, no terrain correction","L4":"Control points used to geolocate image, orthorectified"},"summary":"v"},"cbers:path":{"label":"Reference Grid Path"},"cbers:row":{"label":"Reference Grid Row"},"card4l:specification":{"label":"Specification","mapping":{"SR":"Surface Reflectance (Optical)","ST":"Surface Temperature (Optical)","NRB":"Normalized Radar Backscatter (SAR)","POL":"Polarimetric Radar (SAR)"}},"card4l:specification_version":{"label":"Specification Version"},"card4l:orbit_mean_altitude":{"label":"Platform Altitude","unit":"m"},"card4l:incidence_angle_near_range":{"label":"Incidence Angle (near)","unit":"°"},"card4l:incidence_angle_far_range":{"label":"Incidence Angle (far)","unit":"°"},"card4l:noise_equivalent_intensity":{"label":"Noise Equivalent Intensity","unit":"dB"},"card4l:mean_faraday_rotation_angle":{"label":"Mean Faraday Rotation","unit":"°"},"card4l:speckle_filtering":{"label":"Speckle Filtering","custom":true,"summary":false,"null":"not applied"},"card4l:relative_rtc_accuracy":{"label":"Rel. RTC Accuracy","explain":"Relative accuracy of the Radiometric Terrain Correction","unit":"dB"},"card4l:absolute_rtc_accuracy":{"label":"Abs. RTC Accuracy","explain":"Absolute accuracy of the Radiometric Terrain Correction","unit":"dB"},"card4l:northern_geometric_accuracy":{"label":"Northern Geometric Accuracy","unit":"m"},"card4l:eastern_geometric_accuracy":{"label":"Eastern Geometric Accuracy","unit":"m"},"card4l:ellipsoidal_height":{"label":"Ellipsoidal Height","unit":"m"},"geoadmin:variant":{"label":"Product Variant","mapping":{"krel":"RGB color with relief","komb":"RGB color without relief","kgrel":"Grayscale with relief","kgrs":"Grayscale without relief"}}}} diff --git a/pystac/summaries.py b/pystac/summaries.py index fb09a1f4b..25ce35aab 100644 --- a/pystac/summaries.py +++ b/pystac/summaries.py @@ -1,6 +1,8 @@ from __future__ import annotations +import json import numbers +import sys from abc import abstractmethod from copy import deepcopy from enum import Enum @@ -18,6 +20,11 @@ Union, ) +if sys.version_info[:2] < (3, 9): + from importlib_resources import files as importlib_resources_files +else: + from importlib.resources import files as importlib_resources_files + import pystac from pystac.utils import get_required @@ -26,6 +33,21 @@ from pystac.item import Item +def __getattr__(name: str) -> Any: + if name == "FIELDS_JSON_URL": + import warnings + + warnings.warn( + "FIELDS_JSON_URL is deprecated and will be removed in v2", + DeprecationWarning, + ) + return ( + "https://cdn.jsdelivr.net/npm/@radiantearth/" + "stac-fields/fields-normalized.json" + ) + raise AttributeError(f"module {__name__!r} has no attribute {name!r}") + + class _Comparable_x(Protocol): """Protocol for annotating comparable types. @@ -84,13 +106,17 @@ def __repr__(self) -> str: return self.to_dict().__repr__() -FIELDS_JSON_URL = ( - "https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json" -) - - @lru_cache(maxsize=None) -def _get_fields_json(url: str) -> Dict[str, Any]: +def _get_fields_json(url: Optional[str]) -> Dict[str, Any]: + if url is None: + # Every time pystac is released this file gets pulled from + # https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields/fields-normalized.json + jsonfields: Dict[str, Any] = json.loads( + importlib_resources_files("pystac.static") + .joinpath("fields-normalized.json") + .read_text() + ) + return jsonfields return pystac.StacIO.default().read_json(url) @@ -118,18 +144,7 @@ class Summarizer: summaryfields: Dict[str, SummaryStrategy] def __init__(self, fields: Optional[str] = None): - fieldspath = fields or FIELDS_JSON_URL - try: - jsonfields = _get_fields_json(fieldspath) - except Exception as e: - if fields is None: - raise Exception( - "Could not read fields definition file at " - f"{fields} or it is invalid.\n" - "Try using a local fields definition file." - ) - else: - raise e + jsonfields = _get_fields_json(fields) self._set_field_definitions(jsonfields) def _set_field_definitions(self, fields: Dict[str, Any]) -> None: diff --git a/scripts/pull-static b/scripts/pull-static new file mode 100755 index 000000000..3b120727d --- /dev/null +++ b/scripts/pull-static @@ -0,0 +1,11 @@ +#!/bin/bash + +set -e + +VERSION="1.0.0-rc.1" +SRC="https://cdn.jsdelivr.net/npm/@radiantearth/stac-fields@$VERSION/fields-normalized.json" +HERE=$(dirname "$0") +DEST=$(dirname "$HERE")/pystac/static/fields-normalized.json + +echo "Pulling fields-normalized.json from cdn to $DEST" +curl "$SRC" | jq -c '{metadata: .metadata}' > "$DEST" diff --git a/setup.py b/setup.py index 215181c73..526cfc7fc 100644 --- a/setup.py +++ b/setup.py @@ -23,7 +23,10 @@ package_data={"": ["py.typed", "*.jinja2"]}, py_modules=[splitext(basename(path))[0] for path in glob("pystac/*.py")], python_requires=">=3.8", - install_requires=["python-dateutil>=2.7.0"], + install_requires=[ + "importlib-resources>=5.12.0; python_version<'3.9'", + "python-dateutil>=2.7.0", + ], extras_require={ "validation": ["jsonschema>=4.0.1"], "orjson": ["orjson>=3.5"], diff --git a/tests/test_summaries.py b/tests/test_summaries.py index 1ff2463e6..e5cbaf6b2 100644 --- a/tests/test_summaries.py +++ b/tests/test_summaries.py @@ -36,19 +36,19 @@ def test_summary_wrong_custom_fields_file(self) -> None: Summarizer("wrong/path").summarize(coll.get_all_items()) self.assertTrue("No such file or directory" in str(context.exception)) - def test_cannot_open_fields_file(self) -> None: + def test_can_open_fields_file_even_with_no_nework(self) -> None: old_socket = socket.socket + try: - class no_network(socket.socket): - def __init__(self, *args: Any, **kwargs: Any): - raise Exception("Network call blocked") + class no_network(socket.socket): + def __init__(self, *args: Any, **kwargs: Any): + raise Exception("Network call blocked") - socket.socket = no_network # type:ignore - - with self.assertRaises(Exception) as context: + socket.socket = no_network # type:ignore Summarizer() - socket.socket = old_socket # type:ignore - self.assertTrue("Could not read fields definition" in str(context.exception)) + finally: + # even if this test fails, it should not break the whole test suite + socket.socket = old_socket # type:ignore def test_summary_empty(self) -> None: summaries = Summaries.empty()