From 8f246f2f41b2e6ea108bc4d91e2b800e247d42ea Mon Sep 17 00:00:00 2001 From: Ferdinando Villa Date: Mon, 16 Sep 2024 16:24:27 +0200 Subject: [PATCH] Space implementation from geometry --- .../api/geometry/impl/GeometryBuilder.java | 18 +- .../klab/api/geometry/impl/GeometryImpl.java | 446 +++++++++-------- .../api/knowledge/impl}/QuantityImpl.java | 5 +- .../klab/api/lang/Quantity.java | 23 +- .../klab/api/utils/Utils.java | 13 + .../common/knowledge/KnowledgeRepository.java | 31 ++ .../klab/runtime/scale/ScaleImpl.java | 17 +- .../klab/runtime/scale/space/GridImpl.java | 24 + .../klab/runtime/scale/space/SpaceImpl.java | 462 ++++++++++-------- .../klab/runtime/scale/time/TimeImpl.java | 5 + .../resources/lang/LanguageAdapter.java | 2 +- 11 files changed, 617 insertions(+), 429 deletions(-) rename {klab.core.common/src/main/java/org/integratedmodelling/common/lang => klab.core.api/src/main/java/org/integratedmodelling/klab/api/knowledge/impl}/QuantityImpl.java (90%) create mode 100644 klab.core.common/src/main/java/org/integratedmodelling/common/knowledge/KnowledgeRepository.java diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryBuilder.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryBuilder.java index 2fb7c579f..991bdf149 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryBuilder.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryBuilder.java @@ -10,6 +10,7 @@ import org.integratedmodelling.klab.api.geometry.impl.GeometryImpl.DimensionImpl; import org.integratedmodelling.klab.api.knowledge.observation.scale.time.Time; import org.integratedmodelling.klab.api.knowledge.observation.scale.time.TimeInstant; +import org.integratedmodelling.klab.api.lang.Quantity; /** * Builder for geometries to ease defining time and space extents in forms that @@ -155,23 +156,28 @@ public SpaceBuilder size(long n) { * Bounding box as a double[]{minX, maxX, minY, maxY}; lat/lon use lon as x axis */ public SpaceBuilder boundingBox(double x1, double x2, double y1, double y2) { - space.getParameters().put(GeometryImpl.PARAMETER_SPACE_BOUNDINGBOX, new double[] { x1, x2, y1, y2 }); + space.getParameters().put(GeometryImpl.PARAMETER_SPACE_BOUNDINGBOX, List.of(x1, x2, y1, y2)); return this; } public SpaceBuilder shape(String wktb) { - - space.getParameters().put(GeometryImpl.PARAMETER_SPACE_SHAPE, GeometryImpl.encodeForSerialization(wktb)); + space.getParameters().put(GeometryImpl.PARAMETER_SPACE_SHAPE, wktb); return this; } public SpaceBuilder urn(String urn) { - space.getParameters().put(GeometryImpl.PARAMETER_SPACE_RESOURCE_URN, - GeometryImpl.encodeForSerialization(urn)); + space.getParameters().put(GeometryImpl.PARAMETER_SPACE_RESOURCE_URN, urn); + return this; + } + + public SpaceBuilder resolution(Quantity gridResolution) { + space.setRegular(true); + space.getParameters().put(GeometryImpl.PARAMETER_SPACE_GRIDRESOLUTION, gridResolution); return this; } public SpaceBuilder resolution(String gridResolution) { + space.setRegular(true); space.getParameters().put(GeometryImpl.PARAMETER_SPACE_GRIDRESOLUTION, gridResolution); return this; } @@ -187,7 +193,6 @@ public GeometryBuilder build() { * by the EPSG: projection). The resulting * * @param urn - * @param resolution a string in the format "1 km" * @return */ public GeometryBuilder region(String urn) { @@ -202,7 +207,6 @@ public GeometryBuilder region(String urn) { * resolution. The box is "straight" with the X axis specifying * longitude. * - * @param resolution a string in the format "1 km" * @return */ public GeometryBuilder grid(double x1, double x2, double y1, double y2) { diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryImpl.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryImpl.java index 6b9f4e03b..6ef595b31 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryImpl.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/geometry/impl/GeometryImpl.java @@ -20,7 +20,8 @@ public class GeometryImpl implements Geometry { private static final long serialVersionUID = 8430057200107796568L; /** - * These are useful for common geometry creation from programs. Append to a space definition within curly brackets. + * These are useful for common geometry creation from programs. Append to a space definition within curly + * brackets. */ public static final String WORLD_BBOX_PARAMETERS = "bbox=[-180.0 180.0 -90.0 90.0],proj=EPSG:4326"; @@ -29,8 +30,8 @@ public void setEmpty(boolean b) { } /** - * An internal descriptor for a locator. The API requires using this to disambiguate locators that use world - * coordinates. + * An internal descriptor for a locator. The API requires using this to disambiguate locators that use + * world coordinates. * * @author ferdinando.villa */ @@ -83,7 +84,8 @@ private DimensionTarget() { } /** - * Public constructor for when this is used by a semantic IExtent to disambiguate a world-coordinate locator. + * Public constructor for when this is used by a semantic IExtent to disambiguate a world-coordinate + * locator. * * @param dimension * @param offsets @@ -98,98 +100,100 @@ public DimensionTarget(Dimension dimension, long[] offsets) { public String toString() { return ""; - } - } - -// public static List separateTargets(Object... locators) { -// List ret = new ArrayList<>(); -// DimensionTarget current = null; -// List numbers = new ArrayList<>(); -// List others = new ArrayList<>(); -// -// boolean haveGeometry = false; -// int nExtents = 0; -// -// if (locators != null) { -// for (int i = 0; i < locators.length; i++) { -// Object o = locators[i]; -// if (o instanceof Number) { -// -// if (current == null) { -// current = new DimensionTarget(); -// } -// -// numbers.add((Number) o); -// -// } else if (o instanceof int[]) { -// for (int of : (int[]) o) { -// numbers.add(of); -// } -// } else if (o instanceof long[]) { -// for (long of : (long[]) o) { -// numbers.add(of); -// } -// } else { -// -// if (current != null) { -// current.defineOffsets(numbers); -// current.defineOthers(others); -// ret.add(current); -// } -// current = new DimensionTarget(); -// numbers.clear(); -// others.clear(); -// -// if (o instanceof Dimension.Type) { -// current.type = (Dimension.Type) o; -// } else if (o instanceof Dimension) { -// nExtents++; -// current.extent = (Dimension) o; -// current.type = ((Dimension) o).getType(); -// } else if (o instanceof Scale) { -// current.geometry = (Scale) o; -// } else if (o instanceof Geometry) { -// haveGeometry = true; -// current.geometry = (Geometry) o; -// } else if (o instanceof Class) { -// if (Space.class.isAssignableFrom((Class) o)) { -// current.type = Dimension.Type.SPACE; -// } else if (Time.class.isAssignableFrom((Class) o)) { -// current.type = Dimension.Type.TIME; -// } else { -// throw new KIllegalArgumentException("illegal locator definition: " + o); -// } -// } else { -// // add to 'weird' stuff for other APIs to process -// others.add(o); -// } -// } -// } -// -// if (current != null) { -// current.defineOffsets(numbers); -// ret.add(current); -// } -// } -// -// if (ret.isEmpty() && !numbers.isEmpty()) { -// current = new DimensionTarget(); -// current.defineOffsets(numbers); -// ret.add(current); -// } -// -// if (haveGeometry && ret.size() > 1) { -// throw new KIllegalArgumentException( -// "locators defined through a concrete geometry cannot have any other locator parameter"); -// } -// if (nExtents > 0 && ret.size() != nExtents) { -// throw new KIllegalArgumentException( -// "locators defined through concrete extents cannot have non-extent locator parameters"); -// } -// -// return ret; -// } + Arrays.toString(offsets)) + ">"; + } + } + + // public static List separateTargets(Object... locators) { + // List ret = new ArrayList<>(); + // DimensionTarget current = null; + // List numbers = new ArrayList<>(); + // List others = new ArrayList<>(); + // + // boolean haveGeometry = false; + // int nExtents = 0; + // + // if (locators != null) { + // for (int i = 0; i < locators.length; i++) { + // Object o = locators[i]; + // if (o instanceof Number) { + // + // if (current == null) { + // current = new DimensionTarget(); + // } + // + // numbers.add((Number) o); + // + // } else if (o instanceof int[]) { + // for (int of : (int[]) o) { + // numbers.add(of); + // } + // } else if (o instanceof long[]) { + // for (long of : (long[]) o) { + // numbers.add(of); + // } + // } else { + // + // if (current != null) { + // current.defineOffsets(numbers); + // current.defineOthers(others); + // ret.add(current); + // } + // current = new DimensionTarget(); + // numbers.clear(); + // others.clear(); + // + // if (o instanceof Dimension.Type) { + // current.type = (Dimension.Type) o; + // } else if (o instanceof Dimension) { + // nExtents++; + // current.extent = (Dimension) o; + // current.type = ((Dimension) o).getType(); + // } else if (o instanceof Scale) { + // current.geometry = (Scale) o; + // } else if (o instanceof Geometry) { + // haveGeometry = true; + // current.geometry = (Geometry) o; + // } else if (o instanceof Class) { + // if (Space.class.isAssignableFrom((Class) o)) { + // current.type = Dimension.Type.SPACE; + // } else if (Time.class.isAssignableFrom((Class) o)) { + // current.type = Dimension.Type.TIME; + // } else { + // throw new KIllegalArgumentException("illegal locator definition: " + o); + // } + // } else { + // // add to 'weird' stuff for other APIs to process + // others.add(o); + // } + // } + // } + // + // if (current != null) { + // current.defineOffsets(numbers); + // ret.add(current); + // } + // } + // + // if (ret.isEmpty() && !numbers.isEmpty()) { + // current = new DimensionTarget(); + // current.defineOffsets(numbers); + // ret.add(current); + // } + // + // if (haveGeometry && ret.size() > 1) { + // throw new KIllegalArgumentException( + // "locators defined through a concrete geometry cannot have any other locator + // parameter"); + // } + // if (nExtents > 0 && ret.size() != nExtents) { + // throw new KIllegalArgumentException( + // "locators defined through concrete extents cannot have non-extent locator + // parameters"); + // } + // + // return ret; + // } /** * Bounding box as a double[]{minX, maxX, minY, maxY} @@ -226,13 +230,19 @@ public String toString() { */ public static final String PARAMETER_SPACE_GRIDRESOLUTION = "sgrid"; + /** + * The URN of a grid object to align with. Requires a scope with a valid resource service for resolution. + */ + public static final String PARAMETER_SPACE_GRIDURN = "gridurn"; + /** * Shape specs in WKB */ public static final String PARAMETER_SPACE_SHAPE = "shape"; /** - * Resource URN to retrieve the space extent + * The URN of a resource whose spatial extent will be extracted. Requires a scope with a valid resource + * service for resolution. */ public static final String PARAMETER_SPACE_RESOURCE_URN = "urn"; @@ -277,8 +287,8 @@ public String toString() { public static final String PARAMETER_TIME_LOCATOR = "time"; /** - * Time scope unit: one of millennium, century, decade, year, month, week, day, hour, minute, second, millisecond, - * nanosecond; use lowercase values of {@link Resolution}. + * Time scope unit: one of millennium, century, decade, year, month, week, day, hour, minute, second, + * millisecond, nanosecond; use lowercase values of {@link Resolution}. */ public static final String PARAMETER_TIME_SCOPE_UNIT = "tunit"; @@ -292,16 +302,17 @@ private static String encodeDimension(Dimension dim) { String ret = ""; ret += dim.getType() == Dimension.Type.SPACE - ? (dim.isGeneric() ? (dim.isRegular() ? "\u03a3" : "\u03c3") : (dim.isRegular() ? "S" : "s")) - : (dim.getType() == Dimension.Type.TIME - ? (dim.isGeneric() ? (dim.isRegular() ? "\u03a4" : "\u03c4") : (dim.isRegular() ? "T" : "t")) - : /* TODO others */ ""); + ? (dim.isGeneric() ? (dim.isRegular() ? "\u03a3" : "\u03c3") : (dim.isRegular() ? "S" : "s")) + : (dim.getType() == Dimension.Type.TIME + ? (dim.isGeneric() ? (dim.isRegular() ? "\u03a4" : "\u03c4") : (dim.isRegular() ? "T" : + "t")) + : /* TODO others */ ""); ret += dim.getDimensionality(); if (dim.getShape() != null && !isUndefined(dim.getShape())) { ret += "("; for (int i = 0; i < dim.getShape().size(); i++) { ret += (i == 0 ? "" : ",") + (dim.getShape().get(i) == INFINITE_SIZE ? "\u221E" : - ("" + dim.getShape().get(i))); + ("" + dim.getShape().get(i))); } ret += ")"; } @@ -484,8 +495,8 @@ public static GeometryImpl distributedIn(ExtentDimension key) { } /** - * Encode into a string representation. Keys in parameter maps are sorted so the results can be compared for - * equality. + * Encode into a string representation. Keys in parameter maps are sorted so the results can be compared + * for equality. * * @return the string representation for the geometry */ @@ -590,9 +601,9 @@ public void setScalar(boolean scalar) { this.scalar = scalar; } -// public void setCoverage(Double coverage) { -// this.coverage = coverage; -// } + // public void setCoverage(Double coverage) { + // this.coverage = coverage; + // } public static class DimensionImpl implements Dimension { @@ -638,7 +649,7 @@ public boolean isRegular() { @Override public long[] locate(Dimension dimension) { // TODO -// return new long[0]; + // return new long[0]; throw new KlabUnimplementedException("Dimension::locate"); } @@ -792,7 +803,7 @@ public boolean distributed() { private Granularity granularity = Granularity.SINGLE; private GeometryImpl child; private boolean scalar; -// private double coverage = null; + // private double coverage = null; private boolean empty; @@ -800,9 +811,9 @@ public boolean distributed() { // private MultidimensionalCursor cursor; -// public double getCoverage() { -// return this.coverage; -// } + // public double getCoverage() { + // return this.coverage; + // } // @Override public Geometry getChild() { @@ -887,8 +898,8 @@ public GeometryImpl withSpatialParameter(String key, String value) { } /** - * Return self if we have space, otherwise create a spatial dimension according to parameters and return the merged - * geometry. + * Return self if we have space, otherwise create a spatial dimension according to parameters and return + * the merged geometry. * * @return */ @@ -906,8 +917,8 @@ public GeometryImpl spatial(int dimensions, boolean regular) { } /** - * Return self if we have space, otherwise create a spatial dimension according to parameters and return the merged - * geometry. + * Return self if we have space, otherwise create a spatial dimension according to parameters and return + * the merged geometry. * * @return */ @@ -1016,10 +1027,10 @@ private void finishDefinition() { break; } } -// this.coverage = 1.0; -// for (Dimension dim : getDimensions()) { -// this.coverage *= ((DimensionImpl) dim).coverage; -// } + // this.coverage = 1.0; + // for (Dimension dim : getDimensions()) { + // this.coverage *= ((DimensionImpl) dim).coverage; + // } } /* @@ -1155,8 +1166,8 @@ private static Map readParameters(String kvs) { } /** - * Turn encoded entities back into separators used in geometries that would cause conflict in parameters transmitted - * as text. + * Turn encoded entities back into separators used in geometries that would cause conflict in parameters + * transmitted as text. * * @param val * @return @@ -1165,16 +1176,16 @@ public static String decodeForSerialization(String val) { return val.replaceAll(",", ",").replaceAll("&eq;", "="); } - /** - * Translate separators used in geometries into harmless entities so that they won't cause conflict in parameters - * transmitted as text. - * - * @param val - * @return - */ - public static String encodeForSerialization(String val) { - return val.replaceAll(",", ",").replaceAll("=", "&eq;"); - } +// /** +// * Translate separators used in geometries into harmless entities so that they won't cause conflict in +// * parameters transmitted as text. +// * +// * @param val +// * @return +// */ +// public static String encodeForSerialization(String val) { +// return val.replaceAll(",", ",").replaceAll("=", "&eq;"); +// } private static Class getParameterPODType(String kvp) { switch (kvp) { @@ -1270,7 +1281,8 @@ public T as(Class cls) { } else if (OffsetImpl.class.isAssignableFrom(cls)) { if (!hasShape(this)) { throw new KlabIllegalStateException( - "cannot see a geometry as an offset locator unless shape is specified for all extents"); + "cannot see a geometry as an offset locator unless shape is specified for all " + + "extents"); } return (T) new OffsetImpl(this); } @@ -1279,80 +1291,86 @@ public T as(Class cls) { @Override public Geometry at(Locator locator) { -// return at(separateTargets(locators)); -// } -// -// private Locator at(List targets) { -// -// if (!hasShape(this)) { + // return at(separateTargets(locators)); + // } + // + // private Locator at(List targets) { + // + // if (!hasShape(this)) { throw new KlabUnimplementedException("Geometry::at"); -// } + // } /* - TODO if the locator is a dimension, locate the corresponding offsets and produce a subset geometry. If a - geometry, do the same with all offsets. Empty geometry should return self. Non-empty geometry with empty locator should also + TODO if the locator is a dimension, locate the corresponding offsets and produce a subset geometry. + If a + geometry, do the same with all offsets. Empty geometry should return self. Non-empty geometry with + empty locator should also return self. */ /* * dimension-specific targets cannot be combined with an overall targets. */ -// DimensionTarget locator = null; -// for (DimensionTarget target : targets) { -// if (target.type == null) { -// locator = target; -// break; -// } -// } - -// if (locator != null && targets.size() > 1) { -// throw new KIllegalStateException("Geometry cannot be located with both dimension-specific and overall -// locators"); -// } - - -// if (locator != null) { -// return (locator instanceof Geometry g && g.isEmpty()) ? this : new OffsetImpl(this, convertToOffsets -// (locator)); -// } - -// if (!targets.isEmpty()) { -// long[] pos = new long[this.dimensions.size()]; -// int i = 0; -// for (Dimension dimension : this.dimensions) { -// boolean found = false; -// for (DimensionTarget target : targets) { -// -// if (target.coordinates != null || target.otherLocators != null) { -// // we don't handle these here -// throw new KIllegalStateException("Unrecognized locators for a Geometry: check usage"); -// } -// -// if (target.type != null && target.type == dimension.getType()) { -// found = true; -// if (target.offsets != null) { -// if (target.offsets.length > 1) { -// pos[i] = dimension.offset(target.offsets); -// } else { -// pos[i] = target.offsets[0]; -// } -// } else { -// pos[i] = dimension.size() == 1 ? 0 : -1; -// } -// } -// if (!found) { -// pos[i] = dimension.size() == 1 ? 0 : -1; -// } -// } -// i++; -// } -// -// return new OffsetImpl(this, pos); + // DimensionTarget locator = null; + // for (DimensionTarget target : targets) { + // if (target.type == null) { + // locator = target; + // break; + // } + // } + + // if (locator != null && targets.size() > 1) { + // throw new KIllegalStateException("Geometry cannot be located with both + // dimension-specific and overall + // locators"); + // } + + + // if (locator != null) { + // return (locator instanceof Geometry g && g.isEmpty()) ? this : new OffsetImpl(this, + // convertToOffsets + // (locator)); + // } + + // if (!targets.isEmpty()) { + // long[] pos = new long[this.dimensions.size()]; + // int i = 0; + // for (Dimension dimension : this.dimensions) { + // boolean found = false; + // for (DimensionTarget target : targets) { + // + // if (target.coordinates != null || target.otherLocators != null) { + // // we don't handle these here + // throw new KIllegalStateException("Unrecognized locators for a Geometry: + // check usage"); + // } + // + // if (target.type != null && target.type == dimension.getType()) { + // found = true; + // if (target.offsets != null) { + // if (target.offsets.length > 1) { + // pos[i] = dimension.offset(target.offsets); + // } else { + // pos[i] = target.offsets[0]; + // } + // } else { + // pos[i] = dimension.size() == 1 ? 0 : -1; + // } + // } + // if (!found) { + // pos[i] = dimension.size() == 1 ? 0 : -1; + // } + // } + // i++; + // } + // + // return new OffsetImpl(this, pos); } /** * Return the located offsets corresponding to the passed locator. - * TODO turn this into a subsetting operation so that the locator may select multiple offsets in one or more + * TODO turn this into a subsetting operation so that the locator may select multiple offsets in one or + * more * dimensions * * @param locator @@ -1362,9 +1380,9 @@ protected long[] convertToOffsets(Locator locator) { throw new KlabUnimplementedException("Geometry::convertToOffsets"); } -// // no arguments: locate this with this -// return this; -// } + // // no arguments: locate this with this + // return this; + // } // // @Override // public MultidimensionalCursor getCursor() { @@ -1393,13 +1411,13 @@ public boolean isGeneric() { } /** - * Merge in the passed geometry and modify our data accordingly. If the passed geometry is incompatible, return null - * without error. + * Merge in the passed geometry and modify our data accordingly. If the passed geometry is incompatible, + * return null without error. *

- * In general, any geometry can merge in a scalar geometry (an empty one will become scalar); other dimensions can - * be inherited if they are not present, or they must have the same dimensionality in order to be kept without - * error. If the receiver has a generic dimension and the incoming dimension is not generic, the result adopts the - * specific one. + * In general, any geometry can merge in a scalar geometry (an empty one will become scalar); other + * dimensions can be inherited if they are not present, or they must have the same dimensionality in order + * to be kept without error. If the receiver has a generic dimension and the incoming dimension is not + * generic, the result adopts the specific one. *

* If we have a dimension that the other doesn't, just leave it there in the result. * @@ -1449,8 +1467,8 @@ public GeometryImpl merge(Geometry geometry) { } /** - * Return a copy of this geometry after removing the passed dimension. If the dimension isn't there, return a copy - * of this. + * Return a copy of this geometry after removing the passed dimension. If the dimension isn't there, + * return a copy of this. * * @param dim * @return @@ -1475,7 +1493,8 @@ public GeometryImpl without(Dimension.Type dim) { } /** - * Return another geometry where all dimensions that are in the passed one override any we have of the same type. + * Return another geometry where all dimensions that are in the passed one override any we have of the + * same type. * * @param geometry * @return @@ -1542,7 +1561,8 @@ public boolean infiniteTime() { // } /** - * Based on conventions, extract any parameters from a dimension that can provide location within a scale. + * Based on conventions, extract any parameters from a dimension that can provide location within a + * scale. * * @param extent * @return @@ -1650,9 +1670,9 @@ private Dimension addLogicalSpace(GeometryImpl geometry) { return ret; } -// @Override -// public Geometry geometry() { -// return this; -// } + // @Override + // public Geometry geometry() { + // return this; + // } } \ No newline at end of file diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/lang/QuantityImpl.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/knowledge/impl/QuantityImpl.java similarity index 90% rename from klab.core.common/src/main/java/org/integratedmodelling/common/lang/QuantityImpl.java rename to klab.core.api/src/main/java/org/integratedmodelling/klab/api/knowledge/impl/QuantityImpl.java index 08e08eb16..dd8ab4fe0 100644 --- a/klab.core.common/src/main/java/org/integratedmodelling/common/lang/QuantityImpl.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/knowledge/impl/QuantityImpl.java @@ -1,9 +1,12 @@ -package org.integratedmodelling.common.lang; +package org.integratedmodelling.klab.api.knowledge.impl; import org.integratedmodelling.klab.api.lang.Quantity; +import java.io.Serial; + public class QuantityImpl implements Quantity { + @Serial private static final long serialVersionUID = -4049367875348743501L; private String unit; diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/lang/Quantity.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/lang/Quantity.java index 61a5c2e78..ee6c61b40 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/lang/Quantity.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/lang/Quantity.java @@ -1,5 +1,8 @@ package org.integratedmodelling.klab.api.lang; +import org.integratedmodelling.klab.api.knowledge.impl.QuantityImpl; +import org.integratedmodelling.klab.api.utils.Utils; + import java.io.Serializable; /** @@ -31,7 +34,23 @@ public interface Quantity extends Serializable { String getCurrency(); static Quantity parse(String specification) { - // TODO - return null; + int dot = specification.indexOf('.'); + if (dot < 0) { + dot = specification.indexOf(' '); + } + + if (dot > 0) { + String number = specification.substring(0,dot); + String unit = specification.substring(dot +1); + QuantityImpl ret = new QuantityImpl(); + ret.setValue(Utils.Data.parseAsType(number, Double.class)); + if (unit.contains("@")) { + ret.setCurrency(unit); + } else { + ret.setUnit(unit); + } + } + + return null; } } diff --git a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/utils/Utils.java b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/utils/Utils.java index 9d0f7e945..9bde5a864 100644 --- a/klab.core.api/src/main/java/org/integratedmodelling/klab/api/utils/Utils.java +++ b/klab.core.api/src/main/java/org/integratedmodelling/klab/api/utils/Utils.java @@ -4127,6 +4127,19 @@ public static T asType(Object ret, Class cls) { throw new KlabIllegalArgumentException("cannot interpret value " + ret + " as a " + cls.getCanonicalName()); } + /** + * Split into comma-separated tokens and parse each as a double + * @param s + * @return + */ + public static List parseList(String s, Class resultClass) { + var ret = new ArrayList(); + for (String token : s.split(",")) { + ret.add(parseAsType(token.trim(), resultClass)); + } + return ret; + } + @SuppressWarnings("unchecked") public static T parseAsType(String ret, Class cls) { diff --git a/klab.core.common/src/main/java/org/integratedmodelling/common/knowledge/KnowledgeRepository.java b/klab.core.common/src/main/java/org/integratedmodelling/common/knowledge/KnowledgeRepository.java new file mode 100644 index 000000000..2a9fffc52 --- /dev/null +++ b/klab.core.common/src/main/java/org/integratedmodelling/common/knowledge/KnowledgeRepository.java @@ -0,0 +1,31 @@ +package org.integratedmodelling.common.knowledge; + +import org.integratedmodelling.klab.api.knowledge.Knowledge; +import org.integratedmodelling.klab.api.scope.Scope; +import org.integratedmodelling.klab.api.services.resources.ResourceSet; + +/** + * A singleton that ingests {@link ResourceSet}s intelligently and keeps tabs on loaded knowledve. + */ +public enum KnowledgeRepository { + + INSTANCE; + + + /** + * Load what necessary from the passed resource set and update any index. After this has returned true, + * the URNs requested will be available to retrieve from the {@link #resolve(String, Class)} method. + * + * @param resourceSet + * @param scope + * @return + */ + public boolean ingest(ResourceSet resourceSet, Scope scope) { + return false; + } + + T resolve(String urn, Class resultClass) { + return null; + } + +} diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/ScaleImpl.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/ScaleImpl.java index 61e2ad4dc..184d56af8 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/ScaleImpl.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/ScaleImpl.java @@ -183,9 +183,9 @@ public ScaleImpl(Geometry geometry, Scope scope) { List> extents = new ArrayList<>(3); for (Geometry.Dimension dimension : geometry.getDimensions()) { if (dimension.getType() == Type.SPACE) { - extents.add(SpaceImpl.create(dimension)); + extents.add(SpaceImpl.create(dimension, scope)); } else if (dimension.getType() == Type.TIME) { - extents.add(TimeImpl.create(dimension)); + extents.add(TimeImpl.create(dimension, scope)); } else if (dimension.getType() == Type.NUMEROSITY) { // TODO throw new KlabUnimplementedException("numerosity extent"); @@ -196,18 +196,7 @@ public ScaleImpl(Geometry geometry, Scope scope) { public ScaleImpl(Geometry geometry) { - List> extents = new ArrayList<>(3); - for (Geometry.Dimension dimension : geometry.getDimensions()) { - if (dimension.getType() == Type.SPACE) { - extents.add(SpaceImpl.create(dimension)); - } else if (dimension.getType() == Type.TIME) { - extents.add(TimeImpl.create(dimension)); - } else if (dimension.getType() == Type.NUMEROSITY) { - // TODO - throw new KlabUnimplementedException("numerosity extent"); - } - } - define(extents); + this(geometry, null); } public ScaleImpl(List> extents) { diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/GridImpl.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/GridImpl.java index aa4e40a57..bd5a8902c 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/GridImpl.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/GridImpl.java @@ -8,6 +8,9 @@ import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Grid; import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Projection; import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Shape; +import org.integratedmodelling.klab.api.lang.Quantity; +import org.integratedmodelling.klab.api.services.UnitService; +import org.integratedmodelling.klab.configuration.ServiceConfiguration; import org.locationtech.jts.geom.Point; import org.opengis.referencing.crs.CoordinateReferenceSystem; @@ -91,6 +94,27 @@ public GridImpl(Shape shape, double resolutionInM, boolean makeCellsSquare) { adjustEnvelope(shape.getEnvelope(), resolutionInM, makeCellsSquare); } + /** + * Constructor for a fully specified, anchored grid + * + * @param resolutionInM + */ + public GridImpl(Envelope envelope, double resolutionInM, boolean makeCellsSquare) { + this.declaredResolutionM = resolutionInM; + adjustEnvelope(envelope, resolutionInM, makeCellsSquare); + } + + /** + * Constructor for a fully specified, anchored grid + * + */ + public GridImpl(Envelope envelope, Quantity quantity, boolean makeCellsSquare) { + var unitService = ServiceConfiguration.INSTANCE.getService(UnitService.class); + var originalUnit = unitService.getUnit(quantity.getUnit()); + this.declaredResolutionM = unitService.meters().convert(quantity.getValue(), originalUnit).doubleValue(); + adjustEnvelope(envelope, this.declaredResolutionM, makeCellsSquare); + } + public GridImpl(double resolutionInM) { this.declaredResolutionM = resolutionInM; } diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/SpaceImpl.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/SpaceImpl.java index d1085a21b..a947850ab 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/SpaceImpl.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/space/SpaceImpl.java @@ -1,202 +1,282 @@ package org.integratedmodelling.klab.runtime.scale.space; -import java.util.Collection; -import java.util.Iterator; - -import org.integratedmodelling.klab.api.data.mediation.Unit; +import org.integratedmodelling.common.knowledge.KnowledgeRepository; +import org.integratedmodelling.klab.api.configuration.Configuration; +import org.integratedmodelling.klab.api.exceptions.KlabIllegalArgumentException; +import org.integratedmodelling.klab.api.exceptions.KlabUnimplementedException; import org.integratedmodelling.klab.api.geometry.Geometry.Dimension; -import org.integratedmodelling.klab.api.geometry.Locator; import org.integratedmodelling.klab.api.geometry.impl.GeometryImpl; -import org.integratedmodelling.klab.api.knowledge.observation.scale.Extent; -import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Envelope; -import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Projection; -import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Shape; -import org.integratedmodelling.klab.api.knowledge.observation.scale.space.Space; -import org.integratedmodelling.klab.api.lang.LogicalConnector; +import org.integratedmodelling.klab.api.knowledge.observation.scale.space.*; +import org.integratedmodelling.klab.api.lang.Quantity; +import org.integratedmodelling.klab.api.scope.Scope; +import org.integratedmodelling.klab.api.services.ResourcesService; +import org.integratedmodelling.klab.api.utils.Utils; import org.integratedmodelling.klab.runtime.scale.ExtentImpl; import org.locationtech.jts.geom.GeometryFactory; +import java.util.List; + public abstract class SpaceImpl extends ExtentImpl implements Space { - private static final long serialVersionUID = 1L; - - static GeometryFactory gFactory = new GeometryFactory(); - - public SpaceImpl() { - super(Dimension.Type.SPACE); - } - -// Envelope envelope; - -// @Override -// public Space at(Object... locators) { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public int getRank() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public Space collapsed() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public double getDimensionSize() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// public Unit getDimensionUnit() { -// return null; -// } -// -// @Override -// public Space getExtent(long stateIndex) { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public T as(Class cls) { -// // TODO Auto-generated method stub -// return null; -// } - -// @Override -// public boolean contains(Space o) { -// // TODO Auto-generated method stub -// return false; -// } -// -// @Override -// public boolean overlaps(Space o) { -// // TODO Auto-generated method stub -// return false; -// } -// -// @Override -// public boolean intersects(Space o) { -// // TODO Auto-generated method stub -// return false; -// } -// -// @Override -// public Iterator iterator() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public Shape getGeometricShape() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public Envelope getEnvelope() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public Projection getProjection() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public double getStandardizedVolume() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double getStandardizedArea() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double getStandardizedWidth() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double[] getStandardizedCentroid() { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public double getStandardizedHeight() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double getStandardizedDepth() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double getStandardizedLength() { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double getStandardizedDistance(Space extent) { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public double getStandardizedDimension(Locator locator) { -// // TODO Auto-generated method stub -// return 0; -// } -// -// @Override -// public boolean isEmpty() { -// // TODO Auto-generated method stub -// return false; -// } -// -// @Override -// public Extent merge(Extent other, LogicalConnector how) { -// // TODO Auto-generated method stub -// return null; -// } -// -// @Override -// public boolean matches(Collection constraints) { -// // TODO Auto-generated method stub -// return false; -// } - - public static Space create(Dimension dimension) { - - if (dimension.isRegular()) { - - if (dimension.getParameters().containsKey(GeometryImpl.PARAMETER_SPACE_GRIDRESOLUTION)) { - - } else if (dimension.getParameters().containsKey(GeometryImpl.PARAMETER_SPACE_SHAPE)) { - - } - } else if (dimension.size() > 1) { - - } else { - - } - return null; - } + private static final long serialVersionUID = 1L; + + static GeometryFactory gFactory = new GeometryFactory(); + + public SpaceImpl() { + super(Dimension.Type.SPACE); + } + + // Envelope envelope; + + // @Override + // public Space at(Object... locators) { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public int getRank() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public Space collapsed() { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public double getDimensionSize() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // public Unit getDimensionUnit() { + // return null; + // } + // + // @Override + // public Space getExtent(long stateIndex) { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public T as(Class cls) { + // // TODO Auto-generated method stub + // return null; + // } + + // @Override + // public boolean contains(Space o) { + // // TODO Auto-generated method stub + // return false; + // } + // + // @Override + // public boolean overlaps(Space o) { + // // TODO Auto-generated method stub + // return false; + // } + // + // @Override + // public boolean intersects(Space o) { + // // TODO Auto-generated method stub + // return false; + // } + // + // @Override + // public Iterator iterator() { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public Shape getGeometricShape() { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public Envelope getEnvelope() { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public Projection getProjection() { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public double getStandardizedVolume() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double getStandardizedArea() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double getStandardizedWidth() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double[] getStandardizedCentroid() { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public double getStandardizedHeight() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double getStandardizedDepth() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double getStandardizedLength() { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double getStandardizedDistance(Space extent) { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public double getStandardizedDimension(Locator locator) { + // // TODO Auto-generated method stub + // return 0; + // } + // + // @Override + // public boolean isEmpty() { + // // TODO Auto-generated method stub + // return false; + // } + // + // @Override + // public Extent merge(Extent other, LogicalConnector how) { + // // TODO Auto-generated method stub + // return null; + // } + // + // @Override + // public boolean matches(Collection constraints) { + // // TODO Auto-generated method stub + // return false; + // } + + public static Space create(Dimension dimension) { + return create(dimension, null); + } + + public static Space create(Dimension dimension, Scope scope) { + + Space ret = null; + var resourceUrn = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_RESOURCE_URN, + String.class); + var bboxDefinition = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_BOUNDINGBOX); + var spatialShape = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_SHAPE); + var pointDefinition = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_LONLAT, + String.class); + + if (resourceUrn != null) { + if (scope == null || scope.getService(ResourcesService.class) == null) { + throw new KlabIllegalArgumentException("cannot create spatial extent from resource: " + + "resource services not available"); + } + var resource = scope.getService(ResourcesService.class).resolveResource(resourceUrn, scope); + dimension = + resource.getGeometry().getDimensions().stream().filter(d -> d.getType() == Type.SPACE).findAny().get(); + } + + var shapeDefinition = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_SHAPE, String.class); + Projection projection = + new ProjectionImpl(dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_PROJECTION, + "EPSG:4326")); + Envelope envelope = null; + + Shape shape = null; + if (shapeDefinition != null) { + shape = ShapeImpl.create(shapeDefinition); + projection = shape.getProjection(); + envelope = shape.getEnvelope(); + } else if (pointDefinition != null) { + throw new KlabUnimplementedException("cannot create point from lat/lon coordinates definition " + + "yet"); + } + + if (bboxDefinition != null) { + List corners = null; + if (bboxDefinition instanceof List list) { + corners = list; + } else if (bboxDefinition instanceof String string) { + corners = Utils.Data.parseList(string, Double.class); + } + envelope = EnvelopeImpl.create(corners.get(0), corners.get(1), corners.get(2), corners.get(3), + projection); + } + + if (dimension.isRegular()) { + + Grid grid = null; + + var gridResolution = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_GRIDRESOLUTION); + var gridUrn = dimension.getParameters().get(GeometryImpl.PARAMETER_SPACE_GRIDURN, String.class); + Grid imposedGrid = null; + if (gridUrn != null) { + if (scope == null || scope.getService(ResourcesService.class) == null) { + throw new KlabIllegalArgumentException("cannot create spatial extent from resource: " + + "resource services not available"); + } + var definition = scope.getService(ResourcesService.class).resolve(gridUrn, scope); + // TODO ingest the resource set and parse the symbol + if (KnowledgeRepository.INSTANCE.ingest(scope.getService(ResourcesService.class).resolve(gridUrn, scope), scope)) { + throw new KlabUnimplementedException("cannot create grid from definition yet"); + } + } else if (gridResolution != null && envelope != null) { + Quantity resolution = gridResolution instanceof Quantity quantity ? quantity : + Quantity.parse(gridResolution.toString()); + grid = new GridImpl(envelope, resolution, + Boolean.parseBoolean(Configuration.INSTANCE.getProperty(Configuration.KLAB_USE_IN_MEMORY_DATABASE, "true"))); + } else if (spatialShape != null && envelope != null) { + // TODO + // only consider if no grid resolution is provided; may want more flexibility +// grid = new GridImpl() + } + if (grid != null && imposedGrid != null) { + grid = grid.align(imposedGrid); + } + + if (shape != null && grid != null) { + return new TileImpl(shape, grid); + } else if (shape != null) { + return shape; + } + + } else if (dimension.size() > 1) { + throw new KlabUnimplementedException("cannot create point from this definition " + + "yet"); + } else if (shape != null) { + return shape; + } + return null; + } } diff --git a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/time/TimeImpl.java b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/time/TimeImpl.java index b158b177d..6216a3102 100644 --- a/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/time/TimeImpl.java +++ b/klab.core.services/src/main/java/org/integratedmodelling/klab/runtime/scale/time/TimeImpl.java @@ -17,6 +17,7 @@ import org.integratedmodelling.klab.api.lang.Quantity; import org.integratedmodelling.klab.api.lang.ServiceCall; import org.integratedmodelling.common.lang.ServiceCallImpl; +import org.integratedmodelling.klab.api.scope.Scope; import org.integratedmodelling.klab.api.services.UnitService; import org.integratedmodelling.klab.configuration.ServiceConfiguration; import org.integratedmodelling.klab.runtime.scale.ExtentImpl; @@ -483,6 +484,10 @@ public String encode(String language) { } public static Time create(Dimension dimension) { + return create(dimension, null); + } + + public static Time create(Dimension dimension, Scope context) { long[] period = dimension.getParameters().get(GeometryImpl.PARAMETER_TIME_PERIOD, long[].class); String representation = dimension.getParameters().get(GeometryImpl.PARAMETER_TIME_REPRESENTATION, String.class); diff --git a/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/lang/LanguageAdapter.java b/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/lang/LanguageAdapter.java index ae41c214e..5acd8589b 100644 --- a/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/lang/LanguageAdapter.java +++ b/klab.services.resources/src/main/java/org/integratedmodelling/klab/services/resources/lang/LanguageAdapter.java @@ -1,6 +1,5 @@ package org.integratedmodelling.klab.services.resources.lang; -import org.integratedmodelling.common.lang.QuantityImpl; import org.integratedmodelling.common.lang.ServiceCallImpl; import org.integratedmodelling.common.lang.kim.*; import org.integratedmodelling.klab.api.collections.Identifier; @@ -9,6 +8,7 @@ import org.integratedmodelling.klab.api.data.Version; import org.integratedmodelling.klab.api.knowledge.KlabAsset; import org.integratedmodelling.klab.api.knowledge.SemanticType; +import org.integratedmodelling.klab.api.knowledge.impl.QuantityImpl; import org.integratedmodelling.klab.api.lang.Contextualizable; import org.integratedmodelling.klab.api.lang.LogicalConnector; import org.integratedmodelling.klab.api.lang.ServiceCall;