package mil.nga.giat.geowave.adapter.vector.util; import java.net.MalformedURLException; import java.net.URL; import java.net.URLClassLoader; import java.util.List; import java.util.Locale; import java.util.UUID; import mil.nga.giat.geowave.adapter.vector.plugin.GeoWaveGTDataStore; import mil.nga.giat.geowave.core.store.spi.SPIServiceRegistry; import org.apache.commons.lang3.tuple.Pair; import org.apache.commons.vfs2.FileObject; import org.apache.commons.vfs2.impl.VFSClassLoader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.geotools.data.DataUtilities; import org.geotools.factory.GeoTools; import org.geotools.feature.SchemaException; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.geometry.jts.JTS; import org.geotools.referencing.CRS; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.geometry.MismatchedDimensionException; import org.opengis.referencing.FactoryException; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.cs.CoordinateSystem; import org.opengis.referencing.operation.MathTransform; import org.opengis.referencing.operation.TransformException; import com.vividsolutions.jts.geom.Geometry; public class FeatureDataUtils { private final static Logger LOGGER = LoggerFactory.getLogger(FeatureDataUtils.class); private static final Object MUTEX = new Object(); private static boolean classLoaderInitialized = false; public static void initClassLoader() throws MalformedURLException { synchronized (MUTEX) { if (classLoaderInitialized) { return; } ClassLoader classLoader = FeatureDataUtils.class.getClassLoader(); LOGGER.info("Generating patched classloader"); if (classLoader instanceof VFSClassLoader) { final VFSClassLoader cl = (VFSClassLoader) classLoader; final FileObject[] fileObjs = cl.getFileObjects(); final URL[] fileUrls = new URL[fileObjs.length]; for (int i = 0; i < fileObjs.length; i++) { fileUrls[i] = new URL( fileObjs[i].toString()); } final ClassLoader urlCL = java.security.AccessController .doPrivileged(new java.security.PrivilegedAction<URLClassLoader>() { @Override public URLClassLoader run() { final URLClassLoader ucl = new URLClassLoader( fileUrls, cl); return ucl; } }); GeoTools.addClassLoader(urlCL); SPIServiceRegistry.registerClassLoader(urlCL); } classLoaderInitialized = true; } } public static SimpleFeature defaultCRSTransform( final SimpleFeature entry, final SimpleFeatureType persistedType, final SimpleFeatureType reprojectedType, final MathTransform transform ) { // if the feature is in a different coordinate reference system than // EPSG:4326, transform the geometry final CoordinateReferenceSystem crs = entry.getFeatureType().getCoordinateReferenceSystem(); SimpleFeature defaultCRSEntry = entry; if (!GeoWaveGTDataStore.DEFAULT_CRS.equals(crs)) { MathTransform featureTransform = null; if ((persistedType.getCoordinateReferenceSystem() != null) && persistedType.getCoordinateReferenceSystem().equals( crs) && (transform != null)) { // we can use the transform we have already calculated for this // feature featureTransform = transform; } else if (crs != null) { // this feature differs from the persisted type in CRS, // calculate the transform try { featureTransform = CRS.findMathTransform( crs, GeoWaveGTDataStore.DEFAULT_CRS, true); } catch (final FactoryException e) { LOGGER .warn( "Unable to find transform to EPSG:4326, the feature geometry will remain in its original CRS", e); } } if (featureTransform != null) { try { // what should we do besides log a message when an entry // can't be transformed to EPSG:4326 for some reason? // this will clone the feature and retype it to EPSG:4326 defaultCRSEntry = SimpleFeatureBuilder.retype( entry, reprojectedType); // this will transform the geometry defaultCRSEntry.setDefaultGeometry(JTS.transform( (Geometry) entry.getDefaultGeometry(), featureTransform)); } catch (MismatchedDimensionException | TransformException e) { LOGGER .warn( "Unable to perform transform to EPSG:4326, the feature geometry will remain in its original CRS", e); } } } return defaultCRSEntry; } public static String getAxis( final CoordinateReferenceSystem crs ) { // Some geometries do not have a CRS provided. Thus we default to // urn:ogc:def:crs:EPSG::4326 final CoordinateSystem cs = crs == null ? null : crs.getCoordinateSystem(); if (cs != null && cs.getDimension() > 0) return cs.getAxis( 0).getDirection().name().toString(); return "EAST"; } public static SimpleFeatureType decodeType( final String nameSpace, final String typeName, final String typeDescriptor, final String axis ) throws SchemaException { SimpleFeatureType featureType = nameSpace != null && nameSpace.length() > 0 ? DataUtilities.createType( nameSpace, typeName, typeDescriptor) : DataUtilities.createType( typeName, typeDescriptor); final String lCaseAxis = axis.toLowerCase(Locale.ENGLISH); final CoordinateReferenceSystem crs = featureType.getCoordinateReferenceSystem(); final String typeAxis = getAxis(crs); // Default for EPSG:4326 is lat/long, If the provided type was // long/lat, then re-establish the order if (crs != null && crs.getIdentifiers().toString().contains( "EPSG:4326") && !lCaseAxis.equalsIgnoreCase(typeAxis)) { SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder(); builder.init(featureType); try { // truely no way to force lat first // but it is the default in later versions of GeoTools. // this all depends on the authority at the time of creation featureType = SimpleFeatureTypeBuilder.retype( featureType, CRS.decode( "EPSG:4326", lCaseAxis.equals("east"))); } catch (FactoryException e) { throw new SchemaException( "Cannot decode EPSG:4326", e); } } return featureType; } public static SimpleFeature buildFeature( SimpleFeatureType featureType, Pair<String, Object>[] entries ) { List<AttributeDescriptor> descriptors = featureType.getAttributeDescriptors(); Object[] defaults = new Object[descriptors.size()]; int p = 0; for (AttributeDescriptor descriptor : descriptors) { defaults[p++] = descriptor.getDefaultValue(); } final SimpleFeature newFeature = SimpleFeatureBuilder.build( featureType, defaults, UUID.randomUUID().toString()); for (Pair<String, Object> entry : entries) { newFeature.setAttribute( entry.getKey(), entry.getValue()); } return newFeature; } }