Skip to content

Commit c11a3fb

Browse files
committed
Add CovJSON CSV Formatter
1 parent 423f6b7 commit c11a3fb

2 files changed

Lines changed: 132 additions & 0 deletions

File tree

pygeoapi/formatter/csv_.py

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,8 @@ def write(self, options: dict = {}, data: dict = None) -> str:
7171

7272
if 'Feature' in type or 'features' in data:
7373
return self._write_from_geojson(options, data)
74+
elif 'Coverage' in type or 'coverages' in data:
75+
return self._write_from_covjson(options, data)
7476

7577
def _write_from_geojson(
7678
self, options: dict = {}, data: dict = None, is_point=False
@@ -135,7 +137,79 @@ def _add_feature(
135137
LOGGER.error(err)
136138
raise FormatterSerializationError('Error writing CSV output')
137139

140+
def _write_from_covjson(
141+
self, options: dict = {}, data: dict = None
142+
) -> str:
143+
"""
144+
Generate CovJSON data in CSV format
145+
146+
:param options: CSV formatting options
147+
:param data: dict of CovJSON data
148+
149+
:returns: string representation of format
150+
"""
151+
LOGGER.debug('Processing CovJSON data for CSV output')
152+
units = {}
153+
for p, v in data['parameters'].items():
154+
unit = v['unit']['symbol']
155+
if isinstance(unit, dict):
156+
unit = unit.get('value')
157+
158+
units[p] = unit
159+
160+
fields = ['parameter', 'datetime', 'value', 'unit', 'x', 'y']
161+
LOGGER.debug(f'CSV fields: {fields}')
162+
output = io.StringIO()
163+
writer = csv.DictWriter(output, fields)
164+
writer.writeheader()
165+
166+
if data['type'] == 'Coverage':
167+
is_point = 'point' in data['domain']['domainType'].lower()
168+
self._add_coverage(writer, units, data, is_point)
169+
else:
170+
[
171+
self._add_coverage(writer, units, coverage, True)
172+
for coverage in data['coverages']
173+
if 'point' in coverage['domain']['domainType'].lower()
174+
]
138175
return output.getvalue().encode('utf-8')
139176

177+
@staticmethod
178+
def _add_coverage(
179+
writer: csv.DictWriter, units: dict, data: dict, is_point: bool = False
180+
) -> None:
181+
"""
182+
Add coverage data to CSV writer
183+
184+
:param writer: CSV DictWriter
185+
:param units: dict of parameter units
186+
:param data: dict of CovJSON coverage data
187+
:param is_point: whether the coverage is a point coverage
188+
"""
189+
190+
if is_point is False:
191+
LOGGER.warning('Non-point coverages not supported for CSV output')
192+
return
193+
194+
axes = data['domain']['axes']
195+
time_range = range(len(axes['t']['values']))
196+
197+
try:
198+
[
199+
writer.writerow({
200+
'parameter': parameter,
201+
'datetime': axes['t']['values'][time_value],
202+
'value': data['ranges'][parameter]['values'][time_value],
203+
'unit': units[parameter],
204+
'x': axes['x']['values'][-1],
205+
'y': axes['y']['values'][-1]
206+
})
207+
for parameter in data['ranges']
208+
for time_value in time_range
209+
]
210+
except ValueError as err:
211+
LOGGER.error(err)
212+
raise FormatterSerializationError('Error writing CSV output')
213+
140214
def __repr__(self):
141215
return f'<CSVFormatter> {self.name}'

tests/formatter/test_csv__formatter.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,61 @@ def test_invalid_geometry_data(invalid_geometry_data):
138138
formatter = CSVFormatter({'geom': True})
139139
with pytest.raises(FormatterSerializationError):
140140
formatter.write(data=invalid_geometry_data)
141+
142+
143+
@pytest.fixture
144+
def point_coverage_data():
145+
return {
146+
'type': 'Coverage',
147+
'domain': {
148+
'type': 'Domain',
149+
'domainType': 'PointSeries',
150+
'axes': {
151+
'x': {'values': [-10.1]},
152+
'y': {'values': [-40.2]},
153+
't': {'values': [
154+
'2013-01-01', '2013-01-02', '2013-01-03',
155+
'2013-01-04', '2013-01-05', '2013-01-06']}
156+
}
157+
},
158+
'parameters': {
159+
'PSAL': {
160+
'type': 'Parameter',
161+
'description': {'en': 'The measured salinity'},
162+
'unit': {'symbol': 'psu'},
163+
'observedProperty': {
164+
'id': 'http://vocab.nerc.ac.uk/standard_name/sea_water_salinity/', # noqa
165+
'label': {'en': 'Sea Water Salinity'}
166+
}
167+
}
168+
},
169+
'ranges': {
170+
'PSAL': {
171+
'axisNames': ['t'],
172+
'shape': [6],
173+
'values': [
174+
43.9599, 43.9599, 43.9640, 43.9640, 43.9679, 43.987
175+
]
176+
}
177+
}
178+
}
179+
180+
181+
def test_point_coverage_csv(point_coverage_data):
182+
"""Test CSV output of point coverage data"""
183+
formatter = CSVFormatter({'geom': True})
184+
output = formatter.write(data=point_coverage_data)
185+
csv_reader = DictReader(StringIO(output.decode('utf-8')))
186+
rows = list(csv_reader)
187+
188+
# Verify number of rows
189+
assert len(rows) == 6
190+
191+
# Verify data
192+
first_row = rows[0]
193+
assert first_row['parameter'] == 'PSAL'
194+
assert first_row['datetime'] == '2013-01-01'
195+
assert first_row['value'] == '43.9599'
196+
assert first_row['unit'] == 'psu'
197+
assert first_row['x'] == '-10.1'
198+
assert first_row['y'] == '-40.2'

0 commit comments

Comments
 (0)