Skip to content

Commit

Permalink
Merge pull request #48 from davemfish/bugfix/PGP-requirement-and-tests
Browse files Browse the repository at this point in the history
Bump Pygeoprocessing requirement, and a couple other fixes
  • Loading branch information
phargogh authored Nov 1, 2024
2 parents 413cef3 + f9eac3a commit e8efcc7
Show file tree
Hide file tree
Showing 5 changed files with 66 additions and 25 deletions.
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ GDAL
frictionless
numpy
platformdirs
pygeoprocessing>=2.4.3
pygeoprocessing>=2.4.5
pyyaml
requests
11 changes: 7 additions & 4 deletions src/geometamaker/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,13 @@

LOGGER = logging.getLogger(__name__)

DEFAULT_CONFIG_PATH = os.path.join(
platformdirs.user_config_dir(), 'geometamaker_profile.yml')
CONFIG_FILENAME = 'geometamaker_profile.yml'


class Config(object):
"""Encapsulates user-settings such as a metadata Profile."""

def __init__(self, config_path=DEFAULT_CONFIG_PATH):
def __init__(self, config_path=None):
"""Load a Profile from a config file.
Use a default user profile if none given.
Expand All @@ -24,7 +23,11 @@ def __init__(self, config_path=DEFAULT_CONFIG_PATH):
config_path (str): path to a local yaml file
"""
self.config_path = config_path
if config_path is None:
self.config_path = os.path.join(
platformdirs.user_config_dir(), CONFIG_FILENAME)
else:
self.config_path = config_path
self.profile = models.Profile()

try:
Expand Down
30 changes: 17 additions & 13 deletions src/geometamaker/geometamaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,19 +85,20 @@ def _vsi_path(filepath, scheme):
return filepath


def _wkt_to_epsg_string(wkt_string):
def _wkt_to_epsg_units_string(wkt_string):
crs_string = 'unknown'
units_string = 'unknown'
try:
srs = osr.SpatialReference(wkt_string)
srs.AutoIdentifyEPSG()
crs_string = (
f"{srs.GetAttrValue('AUTHORITY', 0)}:"
f"{srs.GetAttrValue('AUTHORITY', 1)}; "
f"Units:{srs.GetAttrValue('UNITS')}")
f"{srs.GetAttrValue('AUTHORITY', 1)}")
units_string = srs.GetAttrValue('UNIT', 0)
except RuntimeError:
LOGGER.warning(
f'{wkt_string} cannot be interpreted as a coordinate reference system')
return crs_string
return crs_string, units_string


def detect_file_type(filepath, scheme):
Expand Down Expand Up @@ -230,12 +231,13 @@ def describe_vector(source_dataset_path, scheme):
description['schema'] = models.TableSchema(fields=fields)

info = pygeoprocessing.get_vector_info(source_dataset_path)
epsg_string = _wkt_to_epsg_string(info['projection_wkt'])
spatial = {
'bounding_box': models.BoundingBox(*info['bounding_box']),
'crs': epsg_string
}
description['spatial'] = models.SpatialSchema(**spatial)
bbox = models.BoundingBox(*info['bounding_box'])
epsg_string, units_string = _wkt_to_epsg_units_string(
info['projection_wkt'])
description['spatial'] = models.SpatialSchema(
bounding_box=bbox,
crs=epsg_string,
crs_units=units_string)
description['sources'] = info['file_list']
return description

Expand Down Expand Up @@ -269,10 +271,12 @@ def describe_raster(source_dataset_path, scheme):
# Some values of raster info are numpy types, which the
# yaml dumper doesn't know how to represent.
bbox = models.BoundingBox(*[float(x) for x in info['bounding_box']])
epsg_string = _wkt_to_epsg_string(info['projection_wkt'])
epsg_string, units_string = _wkt_to_epsg_units_string(
info['projection_wkt'])
description['spatial'] = models.SpatialSchema(
bounding_box=bbox,
crs=epsg_string)
crs=epsg_string,
crs_units=units_string)
description['sources'] = info['file_list']
return description

Expand Down Expand Up @@ -391,6 +395,6 @@ def describe(source_dataset_path, profile=None):
# Or less common, ValueError if it exists but is incompatible
except (FileNotFoundError, ValueError):
resource = RESOURCE_MODELS[resource_type](**description)
resource = resource.replace(user_profile)

resource = resource.replace(user_profile)
return resource
5 changes: 5 additions & 0 deletions src/geometamaker/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ class SpatialSchema:

bounding_box: BoundingBox
crs: str
crs_units: str


@dataclass
Expand Down Expand Up @@ -247,6 +248,9 @@ class Profile(BaseMetadata):
"""

def __post_init__(self):
super().__post_init__()

@classmethod
def load(cls, filepath):
"""Load metadata document from a yaml file.
Expand Down Expand Up @@ -323,6 +327,7 @@ class Resource(BaseMetadata):
url: str = ''

def __post_init__(self):
super().__post_init__()
self.metadata_path = f'{self.path}.yml'
self.metadata_version: str = f'geometamaker.{geometamaker.__version__}'
self.path = self.path.replace('\\', '/')
Expand Down
43 changes: 36 additions & 7 deletions tests/test_geometamaker.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,11 +213,15 @@ def test_describe_raster(self):
import geometamaker

datasource_path = os.path.join(self.workspace_dir, 'raster.tif')
create_raster(numpy.int16, datasource_path)
create_raster(numpy.int16, datasource_path, projection_epsg=4326)

resource = geometamaker.describe(datasource_path)
self.assertTrue(isinstance(
resource.spatial, geometamaker.models.SpatialSchema))
self.assertRegex(
resource.spatial.crs, r'EPSG:[0-9]*')
self.assertEqual(
resource.spatial.crs_units, 'degree')

resource.write()
self.assertTrue(os.path.exists(f'{datasource_path}.yml'))
Expand Down Expand Up @@ -496,6 +500,27 @@ def test_preexisting_doc_new_fields(self):
field2 = new_resource.get_field_description(field2_name)
self.assertEqual(field2.type, 'String')

def test_preexisting_metadata_document_new_profile(self):
"""Test ammending an existing Metadata document with a profile."""
import geometamaker

title = 'Title'
datasource_path = os.path.join(self.workspace_dir, 'raster.tif')
create_raster(numpy.int16, datasource_path)
resource = geometamaker.describe(datasource_path)
resource.set_title(title)
resource.set_contact(individual_name='alice')
resource.write()

profile = geometamaker.Profile()
profile.set_contact(individual_name='bob')
new_resource = geometamaker.describe(datasource_path, profile=profile)

self.assertEqual(
new_resource.get_title(), title)
self.assertEqual(
new_resource.contact.individual_name, 'bob')

def test_preexisting_incompatible_doc(self):
"""Test when yaml file not created by geometamaker already exists."""
import geometamaker
Expand Down Expand Up @@ -561,7 +586,6 @@ def test_user_configuration(self, mock_user_config_dir):
"""Test existing config populates resource."""
mock_user_config_dir.return_value = self.workspace_dir
import geometamaker
from geometamaker import models

contact = {
'individual_name': 'bob'
Expand All @@ -570,7 +594,7 @@ def test_user_configuration(self, mock_user_config_dir):
'title': 'CC-BY-4'
}

profile = models.Profile()
profile = geometamaker.Profile()
profile.set_contact(**contact)
profile.set_license(**license)

Expand Down Expand Up @@ -655,7 +679,10 @@ def test_invalid_config(self, mock_user_config_dir):
mock_user_config_dir.return_value = self.workspace_dir
import geometamaker.config

with open(geometamaker.config.DEFAULT_CONFIG_PATH, 'w') as file:
config_path = os.path.join(
geometamaker.config.platformdirs.user_config_dir(),
geometamaker.config.CONFIG_FILENAME)
with open(config_path, 'w') as file:
file.write(yaml.dump({'bad': 'data'}))

config = geometamaker.config.Config()
Expand All @@ -668,10 +695,12 @@ def test_delete_config(self, mock_user_config_dir):
mock_user_config_dir.return_value = self.workspace_dir
import geometamaker.config

with open(geometamaker.config.DEFAULT_CONFIG_PATH, 'w') as file:
config_path = os.path.join(
geometamaker.config.platformdirs.user_config_dir(),
geometamaker.config.CONFIG_FILENAME)
with open(config_path, 'w') as file:
file.write(yaml.dump({'bad': 'data'}))

config = geometamaker.config.Config()
config.delete()
self.assertFalse(
os.path.exists(geometamaker.config.DEFAULT_CONFIG_PATH))
self.assertFalse(os.path.exists(config_path))

0 comments on commit e8efcc7

Please sign in to comment.