Skip to content

Commit ec09429

Browse files
committed
handle multiple MCF spatial extents as GeoJSON MultiPolygon geometries
1 parent db2995d commit ec09429

3 files changed

Lines changed: 115 additions & 30 deletions

File tree

pygeometa/schemas/ogcapi_records/__init__.py

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# those files. Users are asked to read the 3rd Party Licenses
1919
# referenced with those assets.
2020
#
21-
# Copyright (c) 2025 Tom Kralidis
21+
# Copyright (c) 2026 Tom Kralidis
2222
#
2323
# Permission is hereby granted, free of charge, to any person
2424
# obtaining a copy of this software and associated documentation
@@ -51,6 +51,7 @@
5151
from pygeometa.core import get_charstring
5252
from pygeometa.helpers import generate_datetime, json_dumps
5353
from pygeometa.schemas.base import BaseOutputSchema
54+
from pygeometa.schemas.util import generate_geometry
5455

5556
THISDIR = os.path.dirname(os.path.realpath(__file__))
5657

@@ -86,21 +87,8 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
8687
self.lang1 = mcf['metadata'].get('language')
8788
self.lang2 = mcf['metadata'].get('language_alternate')
8889

89-
try:
90-
minx, miny, maxx, maxy = (mcf['identification']['extents']
91-
['spatial'][0]['bbox'])
92-
geometry = {
93-
'type': 'Polygon',
94-
'coordinates': [[
95-
[minx, miny],
96-
[minx, maxy],
97-
[maxx, maxy],
98-
[maxx, miny],
99-
[minx, miny]
100-
]]
101-
}
102-
except TypeError:
103-
geometry = None
90+
geometry = generate_geometry(
91+
mcf['identification']['extents']['spatial'])
10492

10593
title = get_charstring(mcf['identification'].get('title'),
10694
self.lang1, self.lang2)

pygeometa/schemas/stac/__init__.py

Lines changed: 8 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
# those files. Users are asked to read the 3rd Party Licenses
1919
# referenced with those assets.
2020
#
21-
# Copyright (c) 2024 Tom Kralidis
21+
# Copyright (c) 2026 Tom Kralidis
2222
#
2323
# Permission is hereby granted, free of charge, to any person
2424
# obtaining a copy of this software and associated documentation
@@ -49,6 +49,7 @@
4949
from pygeometa.core import get_charstring
5050
from pygeometa.helpers import json_dumps
5151
from pygeometa.schemas.base import BaseOutputSchema
52+
from pygeometa.schemas.util import generate_geometry
5253

5354
THISDIR = os.path.dirname(os.path.realpath(__file__))
5455

@@ -81,8 +82,10 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
8182
lang1 = mcf['metadata'].get('language')
8283
lang2 = mcf['metadata'].get('language_alternate')
8384

84-
minx, miny, maxx, maxy = (mcf['identification']['extents']
85-
['spatial'][0]['bbox'])
85+
geometry = generate_geometry(
86+
mcf['identification']['extents']['spatial'])
87+
88+
bbox = mcf['identification']['extents']['spatial'][0]['bbox']
8689

8790
title = get_charstring(mcf['identification'].get('title'),
8891
lang1, lang2)
@@ -93,17 +96,8 @@ def write(self, mcf: dict, stringify: str = True) -> Union[dict, str]:
9396
'stac-version': '1.0.0-beta.2',
9497
'id': mcf['metadata']['identifier'],
9598
'type': 'Feature',
96-
'bbox': [minx, miny, maxx, maxy],
97-
'geometry': {
98-
'type': 'Polygon',
99-
'coordinates': [[
100-
[minx, miny],
101-
[minx, maxy],
102-
[maxx, maxy],
103-
[maxx, miny],
104-
[minx, miny]
105-
]]
106-
},
99+
'bbox': bbox,
100+
'geometry': geometry,
107101
'properties': {
108102
'title': title[0],
109103
'description': description[0],

pygeometa/schemas/util.py

Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
# =================================================================
2+
#
3+
# Terms and Conditions of Use
4+
#
5+
# Unless otherwise noted, computer program source code of this
6+
# distribution # is covered under Crown Copyright, Government of
7+
# Canada, and is distributed under the MIT License.
8+
#
9+
# The Canada wordmark and related graphics associated with this
10+
# distribution are protected under trademark law and copyright law.
11+
# No permission is granted to use them outside the parameters of
12+
# the Government of Canada's corporate identity program. For
13+
# more information, see
14+
# http://www.tbs-sct.gc.ca/fip-pcim/index-eng.asp
15+
#
16+
# Copyright title to all 3rd party software distributed with this
17+
# software is held by the respective copyright holders as noted in
18+
# those files. Users are asked to read the 3rd Party Licenses
19+
# referenced with those assets.
20+
#
21+
# Copyright (c) 2026 Tom Kralidis
22+
#
23+
# Permission is hereby granted, free of charge, to any person
24+
# obtaining a copy of this software and associated documentation
25+
# files (the "Software"), to deal in the Software without
26+
# restriction, including without limitation the rights to use,
27+
# copy, modify, merge, publish, distribute, sublicense, and/or sell
28+
# copies of the Software, and to permit persons to whom the
29+
# Software is furnished to do so, subject to the following
30+
# conditions:
31+
#
32+
# The above copyright notice and this permission notice shall be
33+
# included in all copies or substantial portions of the Software.
34+
#
35+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
36+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
37+
# OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
38+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
39+
# HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
40+
# WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
41+
# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
42+
# OTHER DEALINGS IN THE SOFTWARE.
43+
#
44+
# =================================================================
45+
46+
import logging
47+
48+
LOGGER = logging.getLogger(__name__)
49+
50+
51+
def generate_geometry(spatial: list) -> dict:
52+
"""
53+
Helper function to generate GeoJSON geometry from an
54+
MCF spatial extent
55+
56+
:param spatial: `list` of spatial extents defined
57+
58+
:returns: `dict` of GeoJSON geometry
59+
"""
60+
61+
geometry = None
62+
63+
if len(spatial) == 1:
64+
LOGGER.debug('spatial extent is a default bbox')
65+
try:
66+
minx, miny, maxx, maxy = (spatial[0]['bbox'])
67+
geometry = {
68+
'type': 'Polygon',
69+
'coordinates': [[
70+
[minx, miny],
71+
[minx, maxy],
72+
[maxx, maxy],
73+
[maxx, miny],
74+
[minx, miny]
75+
]]
76+
}
77+
except TypeError:
78+
geometry = None
79+
elif len(spatial) > 1:
80+
LOGGER.debug('spatial extent is a multiple bbox')
81+
bboxes = []
82+
for s in spatial:
83+
try:
84+
minx, miny, maxx, maxy = (s['bbox'])
85+
bboxes.append([[
86+
[minx, miny],
87+
[minx, maxy],
88+
[maxx, maxy],
89+
[maxx, miny],
90+
[minx, miny]
91+
]])
92+
except TypeError:
93+
LOGGER.debug('bbox failed')
94+
95+
geometry = {
96+
'type': 'MultiPolygon',
97+
'coordinates': bboxes
98+
}
99+
else:
100+
LOGGER.debug('Invalid spatial extent')
101+
geometry = None
102+
103+
return geometry

0 commit comments

Comments
 (0)