"Fossies" - the Fresh Open Source Software Archive  

Source code changes of the file "modules/plugin/mongodb/src/main/java/org/geotools/data/mongodb/complex/MongoComplexUtilities.java" between
geotools-24.0-project.zip and geotools-24.1-project.zip

About: GeoTools (The Open Source Java GIS Toolkit) is a Java code library which provides standards compliant methods for the manipulation of geospatial data.

MongoComplexUtilities.java  (geotools-24.0-project):MongoComplexUtilities.java  (geotools-24.1-project)
skipping to change at line 19 skipping to change at line 19
* License as published by the Free Software Foundation; * License as published by the Free Software Foundation;
* version 2.1 of the License. * version 2.1 of the License.
* *
* This library is distributed in the hope that it will be useful, * This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details. * Lesser General Public License for more details.
*/ */
package org.geotools.data.mongodb.complex; package org.geotools.data.mongodb.complex;
import static org.geotools.referencing.CRS.findMathTransform;
import com.mongodb.BasicDBList; import com.mongodb.BasicDBList;
import com.mongodb.DBCursor; import com.mongodb.DBCursor;
import com.mongodb.DBObject; import com.mongodb.DBObject;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.apache.commons.lang3.tuple.MutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.geotools.data.mongodb.AbstractCollectionMapper; import org.geotools.data.mongodb.AbstractCollectionMapper;
import org.geotools.data.mongodb.MongoFeature; import org.geotools.data.mongodb.MongoFeature;
import org.geotools.data.mongodb.MongoGeometryBuilder; import org.geotools.data.mongodb.MongoGeometryBuilder;
import org.geotools.geometry.jts.GeometryCoordinateSequenceTransformer;
import org.geotools.util.logging.Logging; import org.geotools.util.logging.Logging;
import org.locationtech.jts.geom.Geometry;
import org.opengis.feature.Feature; import org.opengis.feature.Feature;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.crs.CoordinateReferenceSystem;
import org.opengis.referencing.operation.MathTransform;
/** This class contains utilities methods for dealing with MongoDB complex featu res. */ /** This class contains utilities methods for dealing with MongoDB complex featu res. */
public final class MongoComplexUtilities { public final class MongoComplexUtilities {
private static final Logger LOG = Logging.getLogger(MongoComplexUtilities.cl ass); private static final Logger LOG = Logging.getLogger(MongoComplexUtilities.cl ass);
// key used to store the parent JSON path in a feature user data map // key used to store the parent JSON path in a feature user data map
public static final String MONGO_PARENT_PATH = "MONGO_PARENT_PATH"; public static final String MONGO_PARENT_PATH = "MONGO_PARENT_PATH";
private static ThreadLocal<
Pair<CoordinateReferenceSystem, GeometryCoordinateSequenceTr
ansformer>>
transformerLocal = new ThreadLocal<>();
private MongoComplexUtilities() {} private MongoComplexUtilities() {}
/** Concat the parent path if it exists to the provided JSON path. */ /** Concat the parent path if it exists to the provided JSON path. */
public static String resolvePath(Feature feature, String jsonPath) { public static String resolvePath(Feature feature, String jsonPath) {
Object parentPath = feature.getUserData().get(MONGO_PARENT_PATH); Object parentPath = feature.getUserData().get(MONGO_PARENT_PATH);
return parentPath == null ? jsonPath : parentPath + "." + jsonPath; return parentPath == null ? jsonPath : parentPath + "." + jsonPath;
} }
/** Store the parent path in a feature user data map. */ /** Store the parent path in a feature user data map. */
public static void setParentPath(Feature feature, String parentPath) { public static void setParentPath(Feature feature, String parentPath) {
feature.getUserData().put(MONGO_PARENT_PATH, parentPath); feature.getUserData().put(MONGO_PARENT_PATH, parentPath);
} }
/** /**
* Will try to extract from the provided object the value that correspond to the given json * Will try to extract from the provided object the value that correspond to the given json
* path. * path.
*/ */
public static Object getValue(Object object, String jsonPath) { public static Object getValue(Object object, String jsonPath) {
// let's make sure we have a feature // let's make sure we have a feature
Feature feature = extractFeature(object, jsonPath); if (!(object instanceof Feature)) {
if (feature instanceof MongoFeature) { // not a feature so nothing to do
throw invalidFeature(object, jsonPath);
}
Feature feature = (Feature) object;
// try before to resolve jsonpath against the feature. If it is SimpleFe
ature
// and we are on the root appSchema object there is no need to retrieve
attributes
// from the DBObject
if (feature instanceof SimpleFeature) {
SimpleFeature sf = (SimpleFeature) feature;
Object value = sf.getAttribute(jsonPath);
if (value != null) return value;
}
Feature extracted = extractFeature(feature, jsonPath);
if (extracted instanceof MongoFeature) {
// no a nested element mongo feature // no a nested element mongo feature
MongoFeature mongoFeature = (MongoFeature) feature; MongoFeature mongoFeature = (MongoFeature) extracted;
return getValue(mongoFeature.getMongoObject(), jsonPath);
// if the feature is a MongoFeature then the geometry attribute
// needs no reprojection
Supplier<GeometryCoordinateSequenceTransformer> transformer =
feature instanceof MongoFeature ? null : getTransformer(feat
ure, mongoFeature);
return getValue(mongoFeature.getMongoObject(), jsonPath, transformer
);
} }
if (feature instanceof MongoCollectionFeature) { if (extracted instanceof MongoCollectionFeature) {
// a mongo feature in the context of a nested element // a mongo feature in the context of a nested element
MongoCollectionFeature collectionFeature = (MongoCollectionFeature) MongoCollectionFeature collectionFeature = (MongoCollectionFeature)
object; extracted;
MongoFeature mongoFeature = collectionFeature.getMongoFeature();
Supplier<GeometryCoordinateSequenceTransformer> transformer =
getTransformer(feature, mongoFeature);
return getValue( return getValue(
collectionFeature.getMongoFeature().getMongoObject(), collectionFeature.getMongoFeature().getMongoObject(),
collectionFeature.getCollectionsIndexes(), collectionFeature.getCollectionsIndexes(),
jsonPath); jsonPath,
transformer);
} }
// could not find a mongo feature, we can do nothing // could not find a mongo feature, we can do nothing
throw invalidFeature(feature, jsonPath); throw invalidFeature(feature, jsonPath);
} }
/** Method for extracting or casting a feature from the provided object. */ static Supplier<GeometryCoordinateSequenceTransformer> getTransformer(
Feature feature, MongoFeature mongoFeature) {
// helper method to retrieve a transformation if the CRS between the Fea
ture
// object and the MongoFeature is different. This might happen if a Comp
lexFeature
// is reprojected. In that case the feature is rebuilt with the MongoFea
ture
// still having a not reprojected geometry.
// The method return a Supplier to allow lazy evaluation and cache the t
ransformer
// in a ThreadLocal to avoid the creation of a new object for each featu
re.
Supplier<GeometryCoordinateSequenceTransformer> transformerSupplier = nu
ll;
CoordinateReferenceSystem crs = mongoFeature.getOriginalCRS();
CoordinateReferenceSystem target =
feature.getDefaultGeometryProperty().getDescriptor().getCoordina
teReferenceSystem();
Pair<CoordinateReferenceSystem, GeometryCoordinateSequenceTransformer> p
air =
transformerLocal.get();
if (pair != null && pair.getLeft().equals(target)) {
final GeometryCoordinateSequenceTransformer cachedTransformer = pair
.getRight();
return () -> cachedTransformer;
}
pair = new MutablePair<>(target, null);
try {
if (crs != null && target != null && !crs.equals(target)) {
MathTransform transform = findMathTransform(crs, target);
GeometryCoordinateSequenceTransformer transformer =
new GeometryCoordinateSequenceTransformer();
transformer.setMathTransform(transform);
pair.setValue(transformer);
// return a supplier to allow lazy evaluation
transformerSupplier = () -> transformer;
}
} catch (FactoryException e) {
LOG.log(
Level.WARNING,
"Unable to find transformation for "
+ crs.getName().getCode()
+ "and "
+ target.getName().getCode());
}
transformerLocal.set(pair);
// store the CRS in the featureType to avoid the rebuilding of the trans
former
// for each feature
return transformerSupplier;
}
public static Feature extractFeature(Object feature, String jsonPath) { public static Feature extractFeature(Object feature, String jsonPath) {
// we should have a feature
if (!(feature instanceof Feature)) { if (!(feature instanceof Feature)) {
// not a feature so nothing to do // not a feature so nothing to do
throw invalidFeature(feature, jsonPath); throw invalidFeature(feature, jsonPath);
} }
return extractFeature((Feature) feature, jsonPath);
}
/** Method for extracting or casting a feature from the provided object. */
public static Feature extractFeature(Feature feature, String jsonPath) {
// let's see if we have the a mongo feature in the user data // let's see if we have the a mongo feature in the user data
Object mongoFeature = Object mongoFeature =
((Feature) feature) feature.getUserData().get(AbstractCollectionMapper.MONGO_OBJECT_
.getUserData() FEATURE_KEY);
.get(AbstractCollectionMapper.MONGO_OBJECT_FEATURE_KEY);
// if we could not find a mongo feature in the user data we stick we the original feature // if we could not find a mongo feature in the user data we stick we the original feature
return mongoFeature == null ? (Feature) feature : (Feature) mongoFeature ; return mongoFeature == null ? feature : (Feature) mongoFeature;
} }
/** /**
* Helper method that creates an exception for when the provided object is n ot of the correct * Helper method that creates an exception for when the provided object is n ot of the correct
* type. * type.
*/ */
private static RuntimeException invalidFeature(Object feature, String jsonPa th) { static RuntimeException invalidFeature(Object feature, String jsonPath) {
return new RuntimeException( return new RuntimeException(
String.format( String.format(
"No possible to obtain a mongo object from '%s' to extra ct '%s'.", "No possible to obtain a mongo object from '%s' to extra ct '%s'.",
feature.getClass(), jsonPath)); feature.getClass(), jsonPath));
} }
/** /**
* Will extract from the mongo db object the value that correspond to the gi ven json path. If * Will extract from the mongo db object the value that correspond to the gi ven json path. If
* the path contain a nested list of values an exception will be throw. * the path contain a nested list of values an exception will be throw.
*/ */
public static Object getValue(DBObject mongoObject, String jsonPath) { public static Object getValue(
return getValue(mongoObject, Collections.emptyMap(), jsonPath); DBObject mongoObject,
String jsonPath,
Supplier<GeometryCoordinateSequenceTransformer> transformer) {
return getValue(mongoObject, Collections.emptyMap(), jsonPath, transform
er);
} }
/** /**
* Will extract from the mongo db object the value that correspond to the gi ven json path. The * Will extract from the mongo db object the value that correspond to the gi ven json path. The
* provided collections indexes will be used to select the proper element fo r the collections * provided collections indexes will be used to select the proper element fo r the collections
* present in the path. * present in the path.
*/ */
public static Object getValue( public static Object getValue(
DBObject mongoObject, Map<String, Integer> collectionsIndexes, Strin DBObject mongoObject,
g jsonPath) { Map<String, Integer> collectionsIndexes,
String jsonPath,
Supplier<GeometryCoordinateSequenceTransformer> transformer) {
MongoObjectWalker walker = new MongoObjectWalker(mongoObject, collection sIndexes, jsonPath); MongoObjectWalker walker = new MongoObjectWalker(mongoObject, collection sIndexes, jsonPath);
// try to convert the founded value to a geometry // try to convert the founded value to a geometry
return convertGeometry(walker.getValue()); return convertGeometry(walker.getValue(), transformer);
} }
/** /**
* Helper method that checks if a mongodb value is a geometry and perform th e proper conversion. * Helper method that checks if a mongodb value is a geometry and perform th e proper conversion.
*/ */
private static Object convertGeometry(Object value) { private static Object convertGeometry(
Object value, Supplier<GeometryCoordinateSequenceTransformer> transf
ormer) {
if (!(value instanceof DBObject) || value instanceof List) { if (!(value instanceof DBObject) || value instanceof List) {
// not a mongodb object or a list of values so nothing to do // not a mongodb object or a list of values so nothing to do
return value; return value;
} }
DBObject object = (DBObject) value; DBObject object = (DBObject) value;
Set keys = object.keySet(); Set keys = object.keySet();
if (keys.size() != 2 || !keys.contains("coordinates") || !keys.contains( "type")) { if (keys.size() != 2 || !keys.contains("coordinates") || !keys.contains( "type")) {
// is mongo db object but not a geometry // is mongo db object but not a geometry
return value; return value;
} }
// we have a geometry so let's try to convert it // we have a geometry so let's try to convert it
MongoGeometryBuilder builder = new MongoGeometryBuilder(); MongoGeometryBuilder builder = new MongoGeometryBuilder();
try { try {
// return the converted geometry // return the converted geometry
return builder.toGeometry(object); Geometry geom = builder.toGeometry(object);
if (transformer != null) {
geom = transformer.get().transform(geom);
}
return geom;
} catch (Exception exception) { } catch (Exception exception) {
// well could not convert the mongo db object to a geometry // well could not convert the mongo db object to a geometry
} }
return value; return value;
} }
/** Utility class class to extract information from a MongoDB object giving a certain path. */ /** Utility class class to extract information from a MongoDB object giving a certain path. */
private static final class MongoObjectWalker { private static final class MongoObjectWalker {
private final Map<String, Integer> collectionsIndexes; private final Map<String, Integer> collectionsIndexes;
skipping to change at line 271 skipping to change at line 367
return getValues(collectionFeature.getMongoFeature().getMongoObject( ), jsonPath); return getValues(collectionFeature.getMongoFeature().getMongoObject( ), jsonPath);
} }
// could not find a mongo feature, we can do nothing // could not find a mongo feature, we can do nothing
throw invalidFeature(feature, jsonPath); throw invalidFeature(feature, jsonPath);
} }
/** /**
* Will extract from the mongo db object all the values that correspond to t he given json path. * Will extract from the mongo db object all the values that correspond to t he given json path.
* If the path contains nested collections the values from all the branches will be merged. * If the path contains nested collections the values from all the branches will be merged.
*/ */
public static Object getValues(DBObject dbObject, String jsonPath) { public static Object getValues(
DBObject dbObject,
String jsonPath,
Supplier<GeometryCoordinateSequenceTransformer> transformer) {
if (jsonPath == null || jsonPath.isEmpty() || dbObject == null) { if (jsonPath == null || jsonPath.isEmpty() || dbObject == null) {
// nothing to do here // nothing to do here
return Collections.emptyList(); return Collections.emptyList();
} }
// let's split the json path in parts which will give us the necessary k eys // let's split the json path in parts which will give us the necessary k eys
String[] jsonPathParts = jsonPath.split("\\."); String[] jsonPathParts = jsonPath.split("\\.");
// recursively get the values using an helper function // recursively get the values using an helper function
List<Object> values = getValuesHelper(dbObject, jsonPathParts, new Array List<Object> values =
List<>(), 0); getValuesHelper(dbObject, jsonPathParts, new ArrayList<>(), 0, t
ransformer);
if (values.size() == 1) { if (values.size() == 1) {
// we only have a single value, let's extract it // we only have a single value, let's extract it
return values.get(0); return values.get(0);
} }
return values; return values;
} }
/** /**
* Helper function that will walk a mongo db object and retrieve all the val ues for a certain * Helper function that will walk a mongo db object and retrieve all the val ues for a certain
* path. * path.
*/ */
private static List<Object> getValuesHelper( private static List<Object> getValuesHelper(
DBObject dbObject, String[] jsonPathParts, List<Object> values, int DBObject dbObject,
index) { String[] jsonPathParts,
List<Object> values,
int index,
Supplier<GeometryCoordinateSequenceTransformer> transformer) {
// get the object corresponding to the current index // get the object corresponding to the current index
Object object = dbObject.get(jsonPathParts[index]); Object object = dbObject.get(jsonPathParts[index]);
if (object == null) { if (object == null) {
// we are done // we are done
return values; return values;
} }
// check if we reach the end of the json path // check if we reach the end of the json path
boolean finalPath = index == jsonPathParts.length - 1; boolean finalPath = index == jsonPathParts.length - 1;
index++; index++;
if (object instanceof List) { if (object instanceof List) {
if (finalPath) { if (finalPath) {
// we reached the end of the json path and we have a list, so le t's add all the // we reached the end of the json path and we have a list, so le t's add all the
// elements of the list // elements of the list
for (Object value : (List) object) { for (Object value : (List) object) {
values.add(convertGeometry(value)); values.add(convertGeometry(value, transformer));
} }
} else { } else {
// well we have a list so we need to interact over each element of the list // well we have a list so we need to interact over each element of the list
for (Object element : (List) object) { for (Object element : (List) object) {
getValuesHelper((DBObject) element, jsonPathParts, values, i ndex); getValuesHelper((DBObject) element, jsonPathParts, values, i ndex, transformer);
} }
} }
} else { } else {
if (finalPath) { if (finalPath) {
// we reached the end of the json path so let's add this object // we reached the end of the json path so let's add this object
values.add(convertGeometry(object)); values.add(convertGeometry(object, transformer));
} else { } else {
// we need to go deeper in this object // we need to go deeper in this object
getValuesHelper((DBObject) object, jsonPathParts, values, index) ; getValuesHelper((DBObject) object, jsonPathParts, values, index, transformer);
} }
} }
// we return the list of founded values for commodity // we return the list of founded values for commodity
return values; return values;
} }
/** Simple method that adds an element ot a json path. */ /** Simple method that adds an element ot a json path. */
private static String concatPath(String parentPath, String path) { private static String concatPath(String parentPath, String path) {
if (parentPath == null || parentPath.isEmpty()) { if (parentPath == null || parentPath.isEmpty()) {
// first element of the path // first element of the path
 End of changes. 30 change blocks. 
31 lines changed or deleted 151 lines changed or added

Home  |  About  |  Features  |  All  |  Newest  |  Dox  |  Diffs  |  RSS Feeds  |  Screenshots  |  Comments  |  Imprint  |  Privacy  |  HTTP(S)