Java tutorial
//@url: http://cida-eros-stash.er.usgs.gov:7990/stash/projects/CIDAGS/repos/cida-geoserver/browse/cida-geoserver-wps/src/main/java/org/geoserver/wps/gs/ImportProcess.java?at=bcfc92d20a41bb8590a22e6e5adf3cb6c22eca5f package gov.usgs.cida.coastalhazards.wps; /* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved. * This code is licensed under the GPL 2.0 license, available at the root * application directory. */ import com.google.common.io.Files; import java.io.File; import java.io.IOException; import java.net.MalformedURLException; import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; import java.util.logging.Level; import java.util.logging.Logger; import org.geoserver.catalog.Catalog; import org.geoserver.catalog.CatalogBuilder; import org.geoserver.catalog.DataStoreInfo; import org.geoserver.catalog.FeatureTypeInfo; import org.geoserver.catalog.LayerInfo; import org.geoserver.catalog.ProjectionPolicy; import org.geoserver.catalog.StyleInfo; import org.geoserver.catalog.WorkspaceInfo; import org.geoserver.config.GeoServerDataDirectory; import org.geoserver.wps.WPSException; import org.geoserver.wps.gs.ImportProcess; import org.geotools.coverage.grid.GridCoverage2D; import org.geotools.data.DataStore; import org.geotools.data.DataUtilities; import org.geotools.data.DefaultTransaction; import org.geotools.data.Transaction; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureStore; import org.geotools.feature.simple.SimpleFeatureBuilder; import org.geotools.feature.simple.SimpleFeatureTypeBuilder; import org.geotools.process.ProcessException; import org.geotools.process.factory.DescribeParameter; import org.geotools.process.factory.DescribeProcess; import org.geotools.process.factory.DescribeResult; import org.geotools.process.gs.GSProcess; import org.geotools.referencing.CRS; import org.geotools.util.logging.Logging; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeDescriptor; import org.opengis.feature.type.GeometryDescriptor; import org.opengis.referencing.crs.CoordinateReferenceSystem; /** * Imports a feature collection into the GeoServer catalog * This process automates the creation of workspaces, stores, and layers given * that they do not yet exist. * TODO: Add coverage into execute properly, right now coverages will not work! * @author Andrea Aime - OpenGeo * */ @DescribeProcess(title = "Import to Catalog", description = "Imports a feature collection into the catalog") public class AutoImportProcess extends ImportProcess { static final Logger LOGGER = Logging.getLogger(AutoImportProcess.class); private Catalog catalog; private GeoServerDataDirectory dataDir; public AutoImportProcess(Catalog catalog) { super(catalog); this.catalog = catalog; this.dataDir = new GeoServerDataDirectory(Files.createTempDir()); } public AutoImportProcess(Catalog catalog, GeoServerDataDirectory dataDirectory) throws IOException { super(catalog); this.catalog = catalog; this.dataDir = dataDirectory; } @DescribeResult(name = "layerName", description = "Name of the new featuretype, with workspace") public String execute( @DescribeParameter(name = "features", description = "Input feature collection") SimpleFeatureCollection features, @DescribeParameter(name = "coverage", min = 0, description = "Input raster") GridCoverage2D coverage, @DescribeParameter(name = "workspace", min = 0, description = "Target workspace (default is the system default)") String workspace, @DescribeParameter(name = "store", min = 0, description = "Target store (default is the workspace default) - if parameter is provided and store does not exist, a best effort attempt will be made to create it") String store, @DescribeParameter(name = "name", min = 0, description = "Name of the new featuretype (default is the name of the features in the collection)") String name, @DescribeParameter(name = "srs", min = 0, description = "Target coordinate reference system (default is based on source when possible)") CoordinateReferenceSystem srs, @DescribeParameter(name = "srsHandling", min = 0, description = "Desired SRS handling (default is FORCE_DECLARED, others are REPROJECT_TO_DECLARED or NONE)") ProjectionPolicy srsHandling, @DescribeParameter(name = "styleName", min = 0, description = "Name of the style to be associated with the layer (default is a standard geometry-specific style)") String styleName) throws ProcessException { // first off, decide what is the target store WorkspaceInfo ws; if (workspace != null) { ws = catalog.getWorkspaceByName(workspace); if (ws == null) { throw new ProcessException("Could not find workspace " + workspace); } } else { ws = catalog.getDefaultWorkspace(); if (ws == null) { throw new ProcessException("The catalog is empty, could not find a default workspace"); } } // ok, find the target store DataStoreInfo storeInfo; if (store != null) { // TODO - This only works for file based data stores. This will not work for // database-backed datastores storeInfo = catalog.getDataStoreByName(ws.getName(), store); if (storeInfo == null) { try { LOGGER.log(Level.INFO, "Store {0} not found. Will try to create", store); File dataRoot = dataDir.findWorkspaceDir(ws); if (dataRoot == null) { dataRoot = new File( dataDir.root() + File.separator + "workspaces" + File.separator + ws.getName()); org.apache.commons.io.FileUtils.forceMkdir(dataRoot); } String dataDirLocation = dataRoot.getPath(); LOGGER.log(Level.INFO, "GEOSERVER_DATA_DIR found @ {0}", dataDirLocation); File storeDirectory = new File(dataRoot, store); if (!storeDirectory.exists()) { LOGGER.log(Level.INFO, "Store directory @ {0} not found. Will try to create", storeDirectory.getPath()); storeDirectory.mkdirs(); } LOGGER.log(Level.INFO, "Store directory @ {0} created", storeDirectory.getPath()); CatalogBuilder builder = new CatalogBuilder(catalog); storeInfo = builder.buildDataStore(store); try { storeInfo.getConnectionParameters().put("url", storeDirectory.toURI().toURL().toExternalForm()); } catch (MalformedURLException ex) { storeInfo.getConnectionParameters().put("url", "file://" + storeDirectory.getPath()); } catalog.add(storeInfo); LOGGER.info("Store created"); } catch (IOException ex) { Logger.getLogger(AutoImportProcess.class.getName()).log(Level.SEVERE, null, ex); } } } else { storeInfo = catalog.getDefaultDataStore(ws); if (storeInfo == null) { throw new ProcessException("Could not find a default store in workspace " + ws.getName()); } } // check if the target layer and the target feature type are not // already there (this is a half-assed attempt as we don't have // an API telling us how the feature type name will be changed // by DataStore.createSchema(...), but better than fully importing // the data into the target store to find out we cannot create the layer...) String tentativeTargetName = null; if (name != null) { tentativeTargetName = ws.getName() + ":" + name; } else { tentativeTargetName = ws.getName() + ":" + features.getSchema().getTypeName(); } if (catalog.getLayer(tentativeTargetName) != null) { throw new ProcessException("Target layer " + tentativeTargetName + " already exists"); } // check the target style if any StyleInfo targetStyle = null; if (styleName != null) { targetStyle = catalog.getStyleByName(styleName); if (targetStyle == null) { throw new ProcessException("Could not find style " + styleName); } } // check the target crs String targetSRSCode = null; if (srs != null) { try { Integer code = CRS.lookupEpsgCode(srs, true); if (code == null) { throw new WPSException("Could not find a EPSG code for " + srs); } targetSRSCode = "EPSG:" + code; } catch (Exception e) { throw new ProcessException("Could not lookup the EPSG code for the provided srs", e); } } else { // check we can extract a code from the original data GeometryDescriptor gd = features.getSchema().getGeometryDescriptor(); if (gd == null) { // data is geometryless, we need a fake SRS targetSRSCode = "EPSG:4326"; srsHandling = ProjectionPolicy.FORCE_DECLARED; } else { CoordinateReferenceSystem nativeCrs = gd.getCoordinateReferenceSystem(); if (nativeCrs == null) { throw new ProcessException( "The original data has no native CRS, " + "you need to specify the srs parameter"); } else { try { Integer code = CRS.lookupEpsgCode(nativeCrs, true); if (code == null) { throw new ProcessException("Could not find an EPSG code for data " + "native spatial reference system: " + nativeCrs); } else { targetSRSCode = "EPSG:" + code; } } catch (Exception e) { throw new ProcessException("Failed to loookup an official EPSG code for " + "the source data native " + "spatial reference system", e); } } } } // import the data into the target store SimpleFeatureType targetType; try { targetType = importDataIntoStore(features, name, storeInfo); } catch (IOException e) { throw new ProcessException("Failed to import data into the target store", e); } // now import the newly created layer into GeoServer try { CatalogBuilder cb = new CatalogBuilder(catalog); cb.setStore(storeInfo); // build the typeInfo and set CRS if necessary FeatureTypeInfo typeInfo = cb.buildFeatureType(targetType.getName()); if (targetSRSCode != null) { typeInfo.setSRS(targetSRSCode); // The import process kills the native project and sets it to the // target projection. The only time this doesn't happen is when a // target SRS isn't passed into the import process. Of course, that's // also not what we want since then we don't have a declared SRS. // Typical requirements for us is to keep the native SRS and reproject // to declared. // // This fixes that, though I'm unsure if this // is desired in every case. - I.S. typeInfo.setNativeCRS(features.getSchema().getGeometryDescriptor().getCoordinateReferenceSystem()); } if (srsHandling != null) { typeInfo.setProjectionPolicy(srsHandling); } // compute the bounds cb.setupBounds(typeInfo); // build the layer and set a style LayerInfo layerInfo = cb.buildLayer(typeInfo); if (targetStyle != null) { layerInfo.setDefaultStyle(targetStyle); } catalog.add(typeInfo); catalog.add(layerInfo); return typeInfo.getPrefixedName(); } catch (Exception e) { throw new ProcessException("Failed to complete the import inside the GeoServer catalog", e); } } private SimpleFeatureType importDataIntoStore(SimpleFeatureCollection features, String name, DataStoreInfo storeInfo) throws IOException, ProcessException { SimpleFeatureType targetType; // grab the data store DataStore ds = (DataStore) storeInfo.getDataStore(null); // decide on the target ft name SimpleFeatureType sourceType = features.getSchema(); if (name != null) { SimpleFeatureTypeBuilder tb = new SimpleFeatureTypeBuilder(); tb.init(sourceType); tb.setName(name); sourceType = tb.buildFeatureType(); } // create the schema ds.createSchema(sourceType); // try to get the target feature type (might have slightly different // name and structure) targetType = ds.getSchema(sourceType.getTypeName()); if (targetType == null) { // ouch, the name was changed... we can only guess now... // try with the typical Oracle mangling targetType = ds.getSchema(sourceType.getTypeName().toUpperCase()); } if (targetType == null) { throw new WPSException("The target schema was created, but with a name " + "that we cannot relate to the one we provided the data store. Cannot proceeed further"); } else { // check the layer is not already there String newLayerName = storeInfo.getWorkspace().getName() + ":" + targetType.getTypeName(); LayerInfo layer = catalog.getLayerByName(newLayerName); // todo: we should not really reach here and know beforehand what the targetType // name is, but if we do we should at least get a way to drop it if (layer != null) { throw new ProcessException("Target layer " + newLayerName + " already exists in the catalog"); } } // try to establish a mapping with old and new attributes. This is again // just guesswork until we have a geotools api that will give us the // exact mapping to be performed Map<String, String> mapping = buildAttributeMapping(sourceType, targetType); // start a transaction and fill the target with the input features Transaction t = new DefaultTransaction(); SimpleFeatureStore fstore = (SimpleFeatureStore) ds.getFeatureSource(targetType.getTypeName()); fstore.setTransaction(t); SimpleFeatureIterator fi = features.features(); SimpleFeatureBuilder fb = new SimpleFeatureBuilder(targetType); while (fi.hasNext()) { SimpleFeature source = fi.next(); fb.reset(); for (String sname : mapping.keySet()) { fb.set(mapping.get(sname), source.getAttribute(sname)); } SimpleFeature target = fb.buildFeature(null); fstore.addFeatures(DataUtilities.collection(target)); } t.commit(); t.close(); return targetType; } /** * Applies a set of heuristics to find which target attribute corresponds to a certain input * attribute * * @param sourceType * @param targetType * @return */ Map<String, String> buildAttributeMapping(SimpleFeatureType sourceType, SimpleFeatureType targetType) { // look for the typical manglings. For example, if the target is a // shapefile store it will move the geometry and name it the_geom // collect the source names Set<String> sourceNames = new HashSet<String>(); for (AttributeDescriptor sd : sourceType.getAttributeDescriptors()) { sourceNames.add(sd.getLocalName()); } // first check if we have been kissed by sheer luck and the names are // the same Map<String, String> result = new HashMap<String, String>(); for (String name : sourceNames) { if (targetType.getDescriptor(name) != null) { result.put(name, name); } } sourceNames.removeAll(result.keySet()); // then check for simple case difference (Oracle case) for (String name : sourceNames) { for (AttributeDescriptor td : targetType.getAttributeDescriptors()) { if (td.getLocalName().equalsIgnoreCase(name)) { result.put(name, td.getLocalName()); break; } } } sourceNames.removeAll(result.keySet()); // then check attribute names being cut (another Oracle case) for (String name : sourceNames) { String loName = name.toLowerCase(); for (AttributeDescriptor td : targetType.getAttributeDescriptors()) { String tdName = td.getLocalName().toLowerCase(); if (loName.startsWith(tdName)) { result.put(name, td.getLocalName()); break; } } } sourceNames.removeAll(result.keySet()); // consider the shapefile geometry descriptor mangling if (targetType.getGeometryDescriptor() != null && "the_geom".equals(targetType.getGeometryDescriptor().getLocalName()) && !"the_geom".equalsIgnoreCase(sourceType.getGeometryDescriptor().getLocalName())) { result.put(sourceType.getGeometryDescriptor().getLocalName(), "the_geom"); } // and finally we return with as much as we can match if (!sourceNames.isEmpty()) { LOGGER.warning("Could not match the following attributes " + sourceNames + " to the target feature type ones: " + targetType); } return result; } }