-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #94 from aodn/feature/6053-query-dataset-with-filter
Feature/6053 query dataset with filter
- Loading branch information
Showing
5 changed files
with
194 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
173 changes: 173 additions & 0 deletions
173
server/src/main/java/au/org/aodn/ogcapi/server/core/util/DatasetSummarizer.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,173 @@ | ||
package au.org.aodn.ogcapi.server.core.util; | ||
|
||
import au.org.aodn.ogcapi.features.model.FeatureCollectionGeoJSON; | ||
import au.org.aodn.ogcapi.features.model.FeatureGeoJSON; | ||
import au.org.aodn.ogcapi.features.model.PointGeoJSON; | ||
import au.org.aodn.ogcapi.server.core.model.enumeration.FeatureProperty; | ||
import lombok.Getter; | ||
|
||
import java.time.YearMonth; | ||
import java.util.HashMap; | ||
import java.util.Map; | ||
|
||
public class DatasetSummarizer { | ||
|
||
private final FeatureCollectionGeoJSON dataset; | ||
|
||
@Getter | ||
private final FeatureCollectionGeoJSON summarizedDataset; | ||
|
||
public DatasetSummarizer(FeatureCollectionGeoJSON dataset) { | ||
this.dataset = dataset; | ||
this.summarizedDataset = new FeatureCollectionGeoJSON(); | ||
this.summarizedDataset.setType(FeatureCollectionGeoJSON.TypeEnum.FEATURECOLLECTION); | ||
summarizeDataset(); | ||
} | ||
|
||
|
||
public void summarizeDataset() { | ||
for (var feature : dataset.getFeatures()) { | ||
var sameLocationFeature = getSameLocationFeature(feature); | ||
if (sameLocationFeature == null) { | ||
summarizedDataset | ||
.getFeatures() | ||
.add(generateNewSummarizedFeature(feature)); | ||
continue; | ||
} | ||
var newFeature = aggregateFeature(sameLocationFeature, feature); | ||
summarizedDataset.getFeatures().remove(sameLocationFeature); | ||
summarizedDataset.getFeatures().add(newFeature); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private FeatureGeoJSON aggregateFeature(FeatureGeoJSON existingFeature, FeatureGeoJSON featureToAdd) { | ||
var aggregatedFeature = new FeatureGeoJSON(); | ||
aggregatedFeature.setType(FeatureGeoJSON.TypeEnum.FEATURE); | ||
aggregatedFeature.setGeometry(existingFeature.getGeometry()); | ||
|
||
try{ | ||
var existingProperties = (Map<String, Object>) existingFeature.getProperties(); | ||
|
||
var newTimeStr = getTime(featureToAdd); | ||
var timeUpdatedProperties = updateTimeRange(existingProperties, newTimeStr); | ||
|
||
var newCount = getCount(featureToAdd); | ||
var timeAndCountUpdatedProperties = updateCount(timeUpdatedProperties, newCount); | ||
|
||
aggregatedFeature.setProperties(timeAndCountUpdatedProperties); | ||
} catch (ClassCastException e) { | ||
throw new RuntimeException("Feature properties is not a map", e); | ||
} | ||
return aggregatedFeature; | ||
} | ||
|
||
private Map<String, Object> updateCount(Map<String, Object> existingProperties, Long newCount) { | ||
var existingCount = (Long) existingProperties.get(FeatureProperty.COUNT.getValue()); | ||
var updatedProperties = new HashMap<>(existingProperties); | ||
updatedProperties.put(FeatureProperty.COUNT.getValue(), existingCount + newCount); | ||
return updatedProperties; | ||
} | ||
|
||
private Map<String, Object> updateTimeRange(Map<String, Object> existingProperties, String newTimeStr) { | ||
|
||
var startTimeStr = (String) existingProperties.get(FeatureProperty.START_TIME.getValue()); | ||
var endTimeStr = (String) existingProperties.get(FeatureProperty.END_TIME.getValue()); | ||
|
||
var updatedProperties = new HashMap<>(existingProperties); | ||
updatedProperties.remove(FeatureProperty.TIME.getValue()); | ||
if (newTimeStr != null) { | ||
var newTime = YearMonth.parse(newTimeStr); | ||
var startTime = YearMonth.parse(startTimeStr); | ||
var endTime = YearMonth.parse(endTimeStr); | ||
if (newTime.isBefore(startTime)) { | ||
startTimeStr = newTimeStr; | ||
} else if (newTime.isAfter(endTime)) { | ||
endTimeStr = newTimeStr; | ||
} | ||
updatedProperties.put(FeatureProperty.START_TIME.getValue(), startTimeStr); | ||
updatedProperties.put(FeatureProperty.END_TIME.getValue(), endTimeStr); | ||
} | ||
return updatedProperties; | ||
} | ||
|
||
|
||
private FeatureGeoJSON generateNewSummarizedFeature(FeatureGeoJSON feature) { | ||
var summarizedFeature = new FeatureGeoJSON(); | ||
summarizedFeature.setType(FeatureGeoJSON.TypeEnum.FEATURE); | ||
summarizedFeature.setGeometry(feature.getGeometry()); | ||
summarizedFeature.setProperties(feature.getProperties()); | ||
var time = getTime(feature); | ||
setPropertyToFeature(feature, FeatureProperty.START_TIME , time); | ||
setPropertyToFeature(feature, FeatureProperty.END_TIME , time); | ||
|
||
return summarizedFeature; | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private void setPropertyToFeature( | ||
FeatureGeoJSON feature, FeatureProperty propertyType, String value | ||
) { | ||
try{ | ||
var properties = (Map<String, Object>) feature.getProperties(); | ||
properties.put(propertyType.getValue(), value); | ||
} catch (ClassCastException e) { | ||
throw new RuntimeException("Feature properties is not a map", e); | ||
} | ||
} | ||
|
||
@SuppressWarnings("unchecked") | ||
private Object getPropertyFromFeature(FeatureGeoJSON feature, FeatureProperty propertyType) { | ||
try{ | ||
var properties = (Map<String, Object>) feature.getProperties(); | ||
return properties.get(propertyType.getValue()); | ||
} catch (ClassCastException e) { | ||
throw new RuntimeException("Feature properties is not a map", e); | ||
} | ||
} | ||
|
||
private Long getCount(FeatureGeoJSON feature) { | ||
try { | ||
return (Long) getPropertyFromFeature(feature, FeatureProperty.COUNT); | ||
} catch (Exception e) { | ||
throw new RuntimeException("failed to get count from feature", e); | ||
} | ||
} | ||
|
||
private String getTime(FeatureGeoJSON feature) { | ||
try { | ||
return (String) getPropertyFromFeature(feature, FeatureProperty.TIME); | ||
} catch (Exception e) { | ||
throw new RuntimeException("failed to get time from feature", e); | ||
} | ||
} | ||
|
||
|
||
private FeatureGeoJSON getSameLocationFeature(FeatureGeoJSON feature) { | ||
for (var f : summarizedDataset.getFeatures()) { | ||
try { | ||
var existingCoordinates = ((PointGeoJSON) f.getGeometry()).getCoordinates(); | ||
var newCoordinates = ((PointGeoJSON) feature.getGeometry()).getCoordinates(); | ||
if (existingCoordinates.get(0).equals(newCoordinates.get(0)) && existingCoordinates.get(1).equals(newCoordinates.get(1))) { | ||
return f; | ||
} | ||
} catch (ClassCastException e) { | ||
throw new RuntimeException("Feature geometry is not a point", e); | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
|
||
} | ||
|
||
// following are values from experience. can change | ||
//1 decimal place: ~11.1 kilometers | ||
//2 decimal places: ~1.1 kilometers | ||
//3 decimal places: ~110 meters | ||
//4 decimal places: ~11 meters | ||
//5 decimal places: ~1.1 meters | ||
|
||
// zoom level less than 7.3, 1 decimal place | ||
// zoom level less than 4, integer | ||
// zoom level less than 1.2, 10 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters