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();
+        }
 
+    }
 }

Reply via email to