This is an automated email from the ASF dual-hosted git repository.
jsorel pushed a commit to branch feat/coverage-json
in repository https://gitbox.apache.org/repos/asf/sis.git
The following commit(s) were added to refs/heads/feat/coverage-json by this
push:
new 12fce9daa9 feat(CoverageJSON): improve gridgeometry and sample
categories mapping
12fce9daa9 is described below
commit 12fce9daa97154c09dd8c16a107b5503173daf6d
Author: jsorel <[email protected]>
AuthorDate: Mon May 22 14:29:48 2023 +0200
feat(CoverageJSON): improve gridgeometry and sample categories mapping
---
.../internal/coveragejson/CoverageResource.java | 66 ++++++++++++++++++++--
.../coveragejson/binding/CategoryEncoding.java | 46 ++++++++++++++-
2 files changed, 106 insertions(+), 6 deletions(-)
diff --git
a/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/CoverageResource.java
b/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/CoverageResource.java
index d1041721c7..8c269bd754 100644
---
a/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/CoverageResource.java
+++
b/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/CoverageResource.java
@@ -46,12 +46,15 @@ import org.apache.sis.coverage.grid.GridRoundingMode;
import org.apache.sis.image.PixelIterator;
import org.apache.sis.internal.coveragejson.binding.Axe;
import org.apache.sis.internal.coveragejson.binding.Axes;
+import org.apache.sis.internal.coveragejson.binding.Category;
+import org.apache.sis.internal.coveragejson.binding.CategoryEncoding;
import org.apache.sis.internal.coveragejson.binding.Coverage;
import org.apache.sis.internal.coveragejson.binding.CoverageJsonObject;
import org.apache.sis.internal.coveragejson.binding.Domain;
import org.apache.sis.internal.coveragejson.binding.GeographicCRS;
import org.apache.sis.internal.coveragejson.binding.IdentifierRS;
import org.apache.sis.internal.coveragejson.binding.NdArray;
+import org.apache.sis.internal.coveragejson.binding.ObservedProperty;
import org.apache.sis.internal.coveragejson.binding.Parameter;
import org.apache.sis.internal.coveragejson.binding.Parameters;
import org.apache.sis.internal.coveragejson.binding.ProjectedCRS;
@@ -59,6 +62,7 @@ import org.apache.sis.internal.coveragejson.binding.Ranges;
import org.apache.sis.internal.coveragejson.binding.ReferenceSystemConnection;
import org.apache.sis.internal.coveragejson.binding.TemporalRS;
import org.apache.sis.internal.coveragejson.binding.VerticalCRS;
+import org.apache.sis.measure.NumberRange;
import org.apache.sis.measure.Units;
import org.apache.sis.referencing.CRS;
import org.apache.sis.referencing.CommonCRS;
@@ -83,7 +87,7 @@ import org.opengis.util.FactoryException;
*
* @author Johann Sorel (Geomatys)
*/
-final class CoverageResource extends AbstractGridCoverageResource {
+public final class CoverageResource extends AbstractGridCoverageResource {
private static final DateTimeFormatter YEAR = new
DateTimeFormatterBuilder()
.appendValue(ChronoField.YEAR, 1, 19, SignStyle.EXCEEDS_PAD)
@@ -353,7 +357,14 @@ final class CoverageResource extends
AbstractGridCoverageResource {
}
return CRS.forCode(jcrs.id);
} else {
- throw new UnsupportedOperationException("Geographic CRS wihout
id not supported");
+ /* Spec 9.5.1.1
+ Note that sometimes (e.g. for numerical model data) the exact
CRS
+ may not be known or may be undefined. In this case the "id" may
+ be omitted, but the "type" still indicates that this is a
geographic CRS.
+ Therefore clients can still use geodetic longitude, geodetic
latitude
+ (and maybe height) axes, even if they cannot accurately
georeference the information.
+ */
+ return CommonCRS.WGS84.normalizedGeographic();
}
} else if (obj instanceof ProjectedCRS) {
final ProjectedCRS jcrs = (ProjectedCRS) obj;
@@ -523,8 +534,8 @@ final class CoverageResource extends
AbstractGridCoverageResource {
gridSize[i] = Math.toIntExact(extent.getSize(i));
scales[i] = d;
offsets[i] = matrix.getElement(i, dimension);
+ continue search;
}
- continue search;
}
throw new DataStoreException("An axe in the Grid to CRS
transform has no scale value");
}
@@ -540,23 +551,28 @@ final class CoverageResource extends
AbstractGridCoverageResource {
//coverage-json expect us to order x/y in longitude/latitude order
if (scrs instanceof org.opengis.referencing.crs.GeographicCRS) {
final org.opengis.referencing.crs.GeographicCRS gcrs =
(org.opengis.referencing.crs.GeographicCRS) scrs;
+ final int gridIdx2 = gridToCrsIndex.get(crsIdx+1);
final GeographicCRS grs = new GeographicCRS();
grs.id = toURI(gcrs);
final ReferenceSystemConnection rsc = new
ReferenceSystemConnection();
rsc.coordinates = Arrays.asList("x", "y");
rsc.system = grs;
binding.referencing.add(rsc);
+ binding.axes.x = buildAxe(gridLow[gridIdx], gridSize[gridIdx],
scales[gridIdx], offsets[gridIdx], false);
+ binding.axes.y = buildAxe(gridLow[gridIdx2],
gridSize[gridIdx2], scales[gridIdx2], offsets[gridIdx2], false);
crsIdx +=2;
} else if (scrs instanceof
org.opengis.referencing.crs.ProjectedCRS) {
final org.opengis.referencing.crs.ProjectedCRS pcrs =
(org.opengis.referencing.crs.ProjectedCRS) scrs;
+ final int gridIdx2 = gridToCrsIndex.get(crsIdx+1);
final ProjectedCRS grs = new ProjectedCRS();
grs.id = toURI(pcrs);
final ReferenceSystemConnection rsc = new
ReferenceSystemConnection();
rsc.coordinates = Arrays.asList("x", "y");
rsc.system = grs;
binding.referencing.add(rsc);
-
+ binding.axes.x = buildAxe(gridLow[gridIdx], gridSize[gridIdx],
scales[gridIdx], offsets[gridIdx], false);
+ binding.axes.y = buildAxe(gridLow[gridIdx2],
gridSize[gridIdx2], scales[gridIdx2], offsets[gridIdx2], false);
crsIdx +=2;
} else if (scrs instanceof
org.opengis.referencing.crs.VerticalCRS) {
@@ -602,6 +618,34 @@ final class CoverageResource extends
AbstractGridCoverageResource {
final Parameter binding = new Parameter();
final String name = sd.getName().toString();
binding.id = name;
+
+ final List<org.apache.sis.coverage.Category> categories =
sd.getCategories();
+ if (categories != null && !categories.isEmpty()) {
+
+ final ObservedProperty obs = new ObservedProperty();
+ obs.id = name;
+ obs.categories = new ArrayList<>();
+ binding.observedProperty = obs;
+
+ final CategoryEncoding catEnc = new CategoryEncoding();
+ binding.categoryEncoding = catEnc;
+ for (org.apache.sis.coverage.Category cat : sd.getCategories()) {
+ final Category catb = new Category();
+ catb.id = cat.getName().toString();
+ obs.categories.add(catb);
+
+ final NumberRange<?> range = cat.getSampleRange();
+ final double min = range.getMinDouble();
+ final double max = range.getMaxDouble(false);
+ final List<Integer> values = new ArrayList<>();
+ for (double i = min; i < max; i++) {
+ values.add((int) i);
+ }
+ catEnc.any.put(catb.id, values);
+ }
+ }
+
+
//TODO convert categories, units,... we might need a database of
observed properties
return new AbstractMap.SimpleImmutableEntry<>(name, binding);
}
@@ -657,7 +701,19 @@ final class CoverageResource extends
AbstractGridCoverageResource {
private static String toURI(CoordinateReferenceSystem crs) throws
FactoryException, DataStoreException {
final Integer code = IdentifiedObjects.lookupEPSG(crs);
- if (code == null) throw new DataStoreException("Could not find EPSG
code for CRS " + crs);
+ if (code == null) {
+ if (crs instanceof org.opengis.referencing.crs.GeographicCRS) {
+ /* Spec 9.5.1.1
+ Note that sometimes (e.g. for numerical model data) the exact
CRS
+ may not be known or may be undefined. In this case the "id" may
+ be omitted, but the "type" still indicates that this is a
geographic CRS.
+ Therefore clients can still use geodetic longitude, geodetic
latitude
+ (and maybe height) axes, even if they cannot accurately
georeference the information.
+ */
+ return null;
+ }
+ throw new DataStoreException("Could not find EPSG code for CRS " +
crs);
+ }
return "http://www.opengis.net/def/crs/EPSG/0/" + code;
}
}
diff --git
a/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/binding/CategoryEncoding.java
b/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/binding/CategoryEncoding.java
index 94cf4a3cc0..3a8c559f46 100644
---
a/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/binding/CategoryEncoding.java
+++
b/storage/sis-coveragejson/src/main/java/org/apache/sis/internal/coveragejson/binding/CategoryEncoding.java
@@ -16,6 +16,19 @@
*/
package org.apache.sis.internal.coveragejson.binding;
+import jakarta.json.bind.annotation.JsonbTypeDeserializer;
+import jakarta.json.bind.annotation.JsonbTypeSerializer;
+import jakarta.json.bind.serializer.DeserializationContext;
+import jakarta.json.bind.serializer.JsonbDeserializer;
+import jakarta.json.bind.serializer.JsonbSerializer;
+import jakarta.json.bind.serializer.SerializationContext;
+import jakarta.json.stream.JsonGenerator;
+import jakarta.json.stream.JsonParser;
+import java.lang.reflect.Type;
+import java.util.Map;
+import
org.apache.sis.internal.coveragejson.binding.CategoryEncoding.Deserializer;
+import
org.apache.sis.internal.coveragejson.binding.CategoryEncoding.Serializer;
+
/**
* CategoryEncoding is an object where each key is equal to an "id" value of
@@ -26,6 +39,37 @@ package org.apache.sis.internal.coveragejson.binding;
*
* @author Johann Sorel (Geomatys)
*/
-public final class CategoryEncoding extends Dictionary<String> {
+@JsonbTypeDeserializer(Deserializer.class)
+@JsonbTypeSerializer(Serializer.class)
+public final class CategoryEncoding extends Dictionary<Object> {
+
+ public static class Deserializer implements
JsonbDeserializer<CategoryEncoding> {
+ @Override
+ public CategoryEncoding deserialize(JsonParser parser,
DeserializationContext ctx, Type rtType) {
+ final CategoryEncoding candidate = new CategoryEncoding();
+ while (parser.hasNext()) {
+ final JsonParser.Event event = parser.next();
+ if (event == JsonParser.Event.KEY_NAME) {
+ // Deserialize inner object
+ final String name = parser.getString();
+ String value = ctx.deserialize(String.class, parser);
+ candidate.setAnyProperty(name, value);
+ }
+ }
+ return candidate;
+ }
+ }
+
+ public static class Serializer implements
JsonbSerializer<CategoryEncoding> {
+
+ @Override
+ public void serialize(CategoryEncoding ranges, JsonGenerator jg,
SerializationContext sc) {
+ jg.writeStartObject();
+ for (Map.Entry<String,Object> entry : ranges.any.entrySet()) {
+ sc.serialize(entry.getKey(), entry.getValue(), jg);
+ }
+ jg.writeEnd();
+ }
+ }
}