tim@616: package de.intevation.gnv.state.layer; tim@616: tim@799: import java.io.File; tim@799: import java.io.IOException; tim@799: import java.io.OutputStream; tim@799: import java.util.Collection; tim@799: import java.util.Iterator; tim@799: tim@799: import org.apache.log4j.Logger; tim@799: import org.w3c.dom.Document; tim@799: import org.w3c.dom.Element; tim@799: import org.w3c.dom.Node; tim@799: sascha@779: import com.vividsolutions.jts.geom.Geometry; tim@649: import com.vividsolutions.jts.io.ParseException; tim@649: import com.vividsolutions.jts.io.WKTReader; tim@649: tim@616: import de.intevation.artifactdatabase.Config; tim@616: import de.intevation.artifactdatabase.XMLUtils; tim@616: import de.intevation.artifacts.ArtifactNamespaceContext; tim@616: import de.intevation.artifacts.CallContext; tim@649: import de.intevation.gnv.artifacts.context.GNVArtifactContext; tim@616: import de.intevation.gnv.geobackend.base.Result; tim@649: import de.intevation.gnv.geobackend.base.query.QueryExecutor; tim@649: import de.intevation.gnv.geobackend.base.query.QueryExecutorFactory; tim@649: import de.intevation.gnv.geobackend.base.query.exception.QueryException; tim@616: import de.intevation.gnv.state.InputData; tim@616: import de.intevation.gnv.state.OutputStateBase; tim@616: import de.intevation.gnv.state.exception.StateException; tim@723: import de.intevation.gnv.utils.ArtifactXMLUtilities; tim@828: import de.intevation.gnv.utils.ExclusiveExec; tim@649: import de.intevation.gnv.utils.FileUtils; tim@649: import de.intevation.gnv.utils.MapfileGenerator; tim@655: import de.intevation.gnv.utils.MetaWriter; tim@649: import de.intevation.gnv.utils.ShapeFileWriter; tim@616: tim@616: /** tim@822: * This OutputState is used for Layer-Products. sascha@780: * @author Tim Englich tim@616: * tim@616: */ tim@616: public class LayerOutputState extends OutputStateBase { tim@616: tim@616: /** tim@616: * the logger, used to log exceptions and additonaly information tim@616: */ tim@616: private static Logger log = Logger.getLogger(LayerOutputState.class); sascha@778: tim@616: /** tim@616: * The UID of this Class. tim@616: */ tim@616: private static final long serialVersionUID = 9180957321704424049L; sascha@778: tim@822: /** tim@822: * The Basename of the Templates for the WMS-Exports tim@822: */ tim@655: public static final String LAYER_MODEL = "layer"; tim@822: tim@822: /** tim@822: * The Name of the Shapefile which will be generated. tim@822: */ tim@822: public static final String SHAPEFILE_NAME = "data.shp"; tim@616: tim@616: /** tim@616: * The ID for the Query fetching the Layer from the DB tim@616: */ sascha@778: private String dataQueryID = null; sascha@778: tim@649: /** tim@649: * The ID for the Query fetching the Geometry from the DB tim@649: * which should be used to Clip the Layerdata tim@649: */ tim@649: private String geometryQueryID = null; sascha@778: tim@822: /** tim@822: * The ID of the Query for fetching the Columnnames of the Table which tim@822: * should put into the Shapefile. tim@822: */ tim@728: private String columnQueryID = null; sascha@778: tim@822: /** tim@822: * The ID of the Query for fetching the Information which kind of Geometry tim@822: * should be put into the Shapefile. tim@822: */ tim@799: private String geometryTypeQueryID = null; tim@799: tim@649: /** tim@822: * The ID for the Value which will hold the Geometry-Value tim@649: */ tim@649: private String geometryID = null; sascha@778: tim@822: /** tim@822: * Flag for synchronized Access of the Shapefile. tim@822: */ tim@649: private Boolean shapeFileLock = new Boolean(true); sascha@778: tim@822: /** tim@822: * The Path where the Shapefile is stored. tim@822: */ tim@649: private String shapeFilePath; sascha@778: tim@822: /** tim@822: * The ID of the Template which should be used to generate the Layerentry tim@822: * in the Mapfile. tim@822: */ tim@799: private String templateID = null; sascha@803: tim@822: /** tim@822: * The Kind of Geometry which should be used to generate the Shapefile. tim@822: */ tim@655: private String geometryType = null; sascha@778: tim@616: /** tim@616: * Constructor tim@616: */ tim@616: public LayerOutputState() { tim@616: super(); tim@616: } tim@616: tim@616: public void out(Document format, Collection inputData, tim@616: OutputStream outputStream, String uuid, tim@616: CallContext callContext) throws StateException { sascha@778: tim@616: log.debug("LayerOutputState.out"); tim@616: String outputMode = XMLUtils.xpathString( tim@616: format, XPATH_OUTPUT_MODE, ArtifactNamespaceContext.INSTANCE); tim@616: if (outputMode.equalsIgnoreCase("wms")) { tim@616: Collection data = this.fetchData(); tim@723: if (data != null && !data.isEmpty()){ tim@799: XMLUtils.toStream(this.getWMS(uuid, callContext, tim@828: data, geometryType, tim@828: inputData), tim@723: outputStream); tim@723: }else{ tim@723: this.writeExceptionReport2Stream(outputStream); tim@723: } tim@616: }else if (outputMode.equalsIgnoreCase("zip")){ tim@616: Collection data = this.fetchData(); tim@723: if (data != null && !data.isEmpty()){ tim@799: this.writeZip(uuid, callContext, outputStream, tim@799: data,geometryType); tim@723: }else{ tim@723: this.writeExceptionReport2Stream(outputStream); tim@723: } sascha@778: tim@616: } tim@616: } tim@723: tim@723: /** ingo@813: * Writes an exception to an output stream. ingo@813: * ingo@813: * @param outputStream The output stream used to write the exception to. tim@723: */ tim@723: private void writeExceptionReport2Stream(OutputStream outputStream) { tim@723: Document document = XMLUtils.newDocument(); ingo@813: ArtifactXMLUtilities. tim@723: createExceptionReport("No Data to Export", document); tim@723: XMLUtils.toStream(document,outputStream); tim@723: } sascha@778: sascha@778: tim@649: /** ingo@813: * Fetches the Data from the Databasebackend. ingo@813: * tim@822: * @return the resultdata. tim@649: */ tim@616: protected Collection fetchData(){ tim@616: log.debug("LayerOutputState.fetchData"); tim@616: Collection result = this.getData(this.queryID); tim@616: Collection data = null; tim@649: String geometryWKT = null; tim@616: if (result != null){ tim@649: QueryExecutor queryExecutor = QueryExecutorFactory.getInstance() tim@649: .getQueryExecutor(); tim@616: Iterator it = result.iterator(); tim@649: String[] queryValues = null; tim@616: if (it.hasNext()){ tim@616: Result resultValue = it.next(); tim@649: String table = resultValue.getString(0); tim@799: tim@799: this.geometryType = this.getGeometryType(table, queryExecutor); tim@799: tim@649: String where = resultValue.getString(1); tim@728: String columns = this.fetchColumns(table); sascha@778: tim@724: templateID = resultValue.getString(2); tim@649: if (this.geometryID != null){ sascha@778: InputData geometryInputData = tim@649: this.inputData.get(this.geometryID); tim@649: if (geometryInputData != null){ sascha@778: tim@649: try { tim@649: Collection geometryData = queryExecutor tim@649: .executeQuery(this.geometryQueryID, tim@649: new String[]{geometryInputData.getValue()}); tim@649: Iterator git = geometryData.iterator(); tim@649: if (git.hasNext()){ tim@649: Result geometryValue = git.next(); tim@649: geometryWKT = geometryValue.getString(0); tim@649: } tim@649: } catch (QueryException e) { tim@649: log.error(e,e); tim@649: } tim@728: queryValues = new String[]{columns, tim@728: table, tim@649: where, tim@649: geometryWKT}; tim@649: }else{ tim@729: //Look into the presetting for an WKT sascha@778: InputData geometryWKTData = this.preSettings != null ? tim@742: this.preSettings.get("geometry") : tim@742: null ; tim@729: if (geometryWKTData != null){ tim@729: queryValues = new String[]{columns, tim@729: table, tim@729: where, tim@729: geometryWKTData.getValue()}; tim@729: }else{ tim@729: queryValues = new String[]{columns,table,where}; tim@729: } tim@729: } tim@729: }else{ tim@729: //Look into the presetting for an WKT sascha@778: InputData geometryWKTData = this.preSettings != null ? tim@742: this.preSettings.get("geometry") : tim@742: null ; tim@729: if (geometryWKTData != null){ tim@729: queryValues = new String[]{columns, tim@729: table, tim@729: where, tim@729: geometryWKTData.getValue()}; tim@729: }else{ tim@728: queryValues = new String[]{columns,table,where}; tim@649: } tim@649: } tim@616: } sascha@778: tim@649: try { tim@649: data = queryExecutor.executeQuery(dataQueryID, tim@649: queryValues); tim@649: if (data != null && geometryWKT != null){ tim@649: WKTReader wktReader = new WKTReader(); tim@649: Geometry border = wktReader.read(geometryWKT); sascha@778: tim@649: Iterator dataIt = data.iterator(); tim@649: while (dataIt.hasNext()){ tim@649: // Trim the Geometries using the tim@649: // Geometry if on is available. tim@649: Result current = dataIt.next(); tim@649: String currentWKT = current.getString(0); tim@649: Geometry currentGeometry = null; tim@649: try { tim@649: currentGeometry = wktReader.read(currentWKT); tim@649: } catch (Exception e) { tim@649: log.error("Error parsing Geometry "+ currentWKT); tim@649: log.error(e,e); tim@649: } sascha@778: tim@649: if (currentGeometry != null){ tim@649: Geometry newGeometry = currentGeometry.intersection(border); tim@649: current.addColumnValue(0, newGeometry.toText()); tim@649: } tim@649: } tim@649: } tim@649: } catch (QueryException e) { tim@649: log.error(e,e); tim@649: } catch (ParseException e){ tim@649: log.error(e,e); tim@649: } tim@616: } tim@616: return data; tim@616: } tim@616: ingo@813: ingo@813: /** ingo@813: * This method determines the geometry type on basis of a table name. ingo@813: * ingo@813: * @param tableName Name of the table in the database. ingo@813: * @param queryExecutor The QueryExecutor. ingo@813: * @return the geometry type as string (e.g. MultiPolygon, Polygon, etc). ingo@813: */ tim@799: private String getGeometryType(String tableName, tim@799: QueryExecutor queryExecutor){ tim@799: String returnValue = null; tim@799: String[] tables = tableName.toUpperCase().split(","); tim@799: String[] filter = tables[0].split("\\."); sascha@803: tim@799: try { sascha@803: Collection result = tim@799: queryExecutor.executeQuery(this.geometryTypeQueryID, filter); tim@799: if (result != null && !result.isEmpty()) tim@799: { tim@799: int geometryCode = result.iterator().next().getInteger(0); sascha@803: if (geometryCode == 11 || tim@799: geometryCode == 10){ tim@799: returnValue = "MultiPolygon"; sascha@803: }else if (geometryCode == 9 || tim@799: geometryCode == 8){ tim@799: returnValue = "MultiLineString"; tim@799: }else if (geometryCode == 7){ tim@799: returnValue = "MultiPoint"; tim@799: }else if (geometryCode == 6){ tim@799: returnValue = "GeometryCollection"; sascha@803: }else if (geometryCode == 5 || tim@799: geometryCode == 4){ tim@799: returnValue = "Polygon"; sascha@803: }else if (geometryCode == 3 || tim@799: geometryCode == 2){ tim@799: returnValue = "LineString"; tim@799: }else if (geometryCode == 1){ tim@799: returnValue = "Point"; tim@799: }else if (geometryCode == 0){ tim@799: returnValue = "Geometry"; tim@799: } tim@799: } tim@799: } catch (QueryException e) { tim@799: log.error(e,e); tim@799: } sascha@803: tim@799: return returnValue; tim@799: } ingo@813: ingo@813: ingo@813: /** ingo@813: * Fetch the columns of a specific table. ingo@813: * ingo@813: * @param tableName The name of the table. ingo@813: * @return the columns as string. ingo@813: */ tim@728: private String fetchColumns(String tableName){ tim@728: String returnValue = null; tim@728: try { tim@756: String[] tables = tableName.toUpperCase().split(","); tim@756: String[] filter = tables[0].split("\\."); tim@756: // Only use the first Table the second one will be ignored. tim@728: QueryExecutor queryExecutor = QueryExecutorFactory.getInstance() tim@728: .getQueryExecutor(); sascha@778: tim@728: Collection columnData = queryExecutor. sascha@778: executeQuery(this.columnQueryID, tim@728: filter); tim@728: if (columnData != null && !columnData.isEmpty()){ tim@728: StringBuffer sb = new StringBuffer(); tim@728: synchronized (sb) { tim@728: Iterator it = columnData.iterator(); tim@728: while(it.hasNext()){ tim@728: Result current = it.next(); tim@728: sb.append(current.getString(0)); tim@728: if (it.hasNext()){ tim@728: sb.append(" , "); tim@728: } tim@728: } tim@728: } tim@728: returnValue = sb.toString(); tim@728: } sascha@778: tim@728: } catch (QueryException e) { tim@728: log.error(e,e); tim@728: } tim@728: return returnValue; tim@728: } ingo@813: ingo@813: tim@616: @Override tim@616: public void setup(Node configuration) { tim@616: log.debug("LayerOutputState.setup"); tim@616: super.setup(configuration); tim@649: this.dataQueryID = Config.getStringXPath(configuration, tim@649: "queryID-layerdata"); tim@649: this.geometryID = Config.getStringXPath(configuration, tim@649: "inputvalue-geometry"); tim@649: this.geometryQueryID = Config.getStringXPath(configuration, tim@649: "queryID-geometry"); sascha@778: tim@728: this.columnQueryID = "layer_colums"; //Config.getStringXPath(configuration, tim@728: // "queryID-columns"); tim@799: this.geometryTypeQueryID = "geometry_type"; tim@616: } sascha@778: ingo@813: ingo@813: /** tim@822: * Write the resultdata to shapefiles. ingo@813: * ingo@813: * @param uuid The UUID of the current artifact. ingo@813: * @param data The finalized data used for shapefile creation. ingo@813: * @param callContext The CallContext object. ingo@813: * @param geometryType The geometry type. ingo@813: * @return the shapefile path. ingo@813: */ tim@649: protected String writeToShapeFile( tim@649: String uuid, tim@649: Collection data, tim@799: CallContext callContext, tim@799: String geometryType tim@649: ) { tim@649: File baseDir = shapefileDirectory(callContext); sascha@778: tim@649: File shapeDir = new File(baseDir, uuid); tim@649: boolean success = false; tim@649: boolean createdDir = false; tim@649: tim@649: try { tim@649: synchronized (shapeFileLock) { tim@828: if (shapeDir.exists()) { tim@828: FileUtils.deleteContent(shapeDir); tim@649: } tim@828: else if (!shapeDir.mkdirs()) { sascha@778: log.error("cannot create directory '" tim@649: + shapeDir.getAbsolutePath() + "'"); tim@649: return null; tim@649: } tim@649: createdDir = true; tim@649: } tim@649: tim@649: File shapeFile = new File(shapeDir, SHAPEFILE_NAME); tim@799: if (!ShapeFileWriter.writeDataToFile(shapeFile, "data", data,geometryType)){ tim@649: log.error("writing data into shapefile failed"); tim@649: return null; tim@649: } sascha@778: tim@649: shapeFilePath = shapeDir.getAbsolutePath(); tim@649: success = true; tim@649: tim@649: callContext.afterCall(CallContext.STORE); tim@649: tim@649: return shapeFilePath; tim@649: } tim@649: finally { tim@649: if (!success && createdDir) { tim@649: FileUtils.deleteRecursive(shapeDir); tim@649: } tim@649: } tim@649: } sascha@778: ingo@813: ingo@813: /** ingo@813: * Create a zip archive with the shapefiles of the given shapefiles path and ingo@813: * write it to output. ingo@813: * ingo@813: * @param uuid The UUID of the current artifact. ingo@813: * @param callContext The CallContext object. ingo@813: * @param output The output stream. ingo@813: * @param data The data to be written to shapefile. ingo@813: * @param geometryType The geometry type. ingo@813: * @throws StateException if an error occured while zipfile creation. ingo@813: */ tim@649: protected void writeZip( tim@649: String uuid, tim@649: CallContext callContext, tim@649: OutputStream output, tim@799: Collection data, tim@799: String geometryType sascha@778: ) tim@649: throws StateException tim@649: { tim@649: try { tim@649: String p = getShapeFilePath(); tim@649: if (p != null) { tim@649: File dir = new File(p); tim@649: if (dir.isDirectory()) { tim@649: FileUtils.createZipArchive(dir, output); tim@649: } tim@649: } tim@649: else { sascha@778: tim@799: if ((p = writeToShapeFile(uuid, data, callContext,geometryType)) != null) { tim@649: FileUtils.createZipArchive(new File(p), output); tim@649: } tim@649: } tim@649: } tim@649: catch (IOException ioe) { tim@649: log.error(ioe.getLocalizedMessage(), ioe); tim@649: } tim@649: } sascha@778: ingo@813: /** ingo@813: * Returns the shapefile path. ingo@813: * ingo@813: * @return the shapefile path. ingo@813: */ tim@649: public String getShapeFilePath() { tim@649: synchronized (shapeFileLock) { tim@649: return shapeFilePath; tim@649: } tim@649: } sascha@778: ingo@813: tim@649: private static File shapefileDirectory(CallContext callContext) { tim@649: // TODO: Refactoring nessessary it should be used only one Shapefilepath tim@649: // for alle Modes. Code was taken from HorizontalCrossSectionMeshOutputState tim@649: GNVArtifactContext context = tim@649: (GNVArtifactContext)callContext.globalContext(); tim@649: File dir = (File)context.get( tim@649: GNVArtifactContext.HORIZONTAL_CROSS_SECTION_RESULT_SHAPEFILE_PATH_KEY); tim@649: return dir != null tim@649: ? dir tim@649: : GNVArtifactContext.DEFAULT_HORIZONTAL_CROSS_SECTION_PROFILE_SHAPEFILE_PATH; tim@649: } sascha@778: ingo@813: tim@649: @Override tim@649: public void endOfLife(Object globalContext) { tim@649: super.endOfLife(globalContext); tim@649: tim@649: // do it in background tim@649: new Thread() { ingo@813: @Override tim@649: public void run() { tim@649: String path = resetShapeFilePath(); tim@649: tim@649: if (path == null) { tim@649: return; tim@649: } tim@649: tim@649: File dir = new File(path); tim@649: tim@649: for (int i = 0; i < 10; ++i) { tim@649: if (!dir.exists() || FileUtils.deleteRecursive(dir)) { tim@649: MapfileGenerator.getInstance().update(); tim@649: return; tim@649: } tim@649: tim@649: try { tim@649: Thread.sleep(10000L); tim@649: } tim@649: catch (InterruptedException ie) { tim@649: } tim@649: } tim@649: tim@649: log.error("failed to remove directory '" + path + "'"); tim@649: } // run tim@649: }.start(); tim@649: } sascha@778: tim@822: /** tim@822: * Resets the Settings e.g shapeFilePath and templateID tim@822: * @return tim@822: */ tim@822: private String resetShapeFilePath() { tim@649: synchronized (shapeFileLock) { tim@649: String path = shapeFilePath; tim@649: shapeFilePath = null; tim@724: templateID = null; tim@649: return path; tim@649: } tim@649: } ingo@813: ingo@813: ingo@813: /** ingo@813: * Write data to shapefiles and feed a map service with information about ingo@813: * these shapefiles. The map service can be queried for displaying ingo@813: * corresponding layers as WMS. ingo@813: * ingo@813: * @param uuid The UUID of the current artifact. ingo@813: * @param callContext The CallContext object. ingo@813: * @param data A collection with some input data. ingo@813: * @param geometryType The geometry type. tim@828: * @param inputData the Parameters which are send by the out-Call. ingo@813: * @return a document with some meta information (shapefile path, geometry ingo@813: * type, time to live of the current artifact, etc). ingo@813: * @throws StateException if an error occured while shapefile writing. ingo@813: */ sascha@778: protected Document getWMS(String uuid, sascha@778: CallContext callContext, tim@799: Collection data, tim@828: String geometryType, tim@828: Collection inputData) tim@655: throws StateException tim@655: { tim@655: Document document = XMLUtils.newDocument(); tim@655: tim@655: Element pathElement = document.createElement("path"); tim@655: document.appendChild(pathElement); tim@655: tim@655: String path = getShapeFilePath(); tim@655: tim@655: if (path != null && new File(path).isDirectory()) { tim@828: String title = getLayerTitle(inputData); tim@828: if (title == null) { tim@828: title = uuid; tim@828: } tim@828: tim@828: callContext.putContextValue( tim@828: MetaWriter.CONTEXT_LAYER_TITLE, title); tim@828: tim@828: String paramType = findParameterTitle(geometryType); tim@828: tim@828: if (log.isDebugEnabled()) { tim@828: log.debug("Layer title: " + title); tim@828: log.debug("Layer type: " + paramType); tim@828: } tim@828: tim@828: Document meta = MetaWriter.writeHorizontalcrosssectionMeta( tim@828: callContext, uuid, path, paramType); tim@828: if (meta != null) { tim@828: MapfileGenerator.getInstance().update(); tim@828: return meta; tim@828: } tim@828: tim@655: pathElement.setTextContent(path); tim@655: } tim@655: else { tim@828: ExclusiveExec.UniqueKey key = ExclusiveExec.INSTANCE.acquire(uuid); tim@828: try{ tim@828: if (data != null && tim@828: (path = writeToShapeFile(uuid, data, callContext,geometryType)) != null) { tim@828: String paramType = findParameterTitle(geometryType); tim@828: String title = getLayerTitle(inputData); tim@828: if (title == null) { tim@828: title = uuid; tim@828: } tim@828: Document meta = MetaWriter.writeLayerMeta(callContext, uuid, tim@828: path, paramType, tim@828: this.determineGeometryType(geometryType)); tim@828: if (meta != null) { tim@828: MapfileGenerator.getInstance().update(); tim@828: return meta; tim@828: } tim@828: pathElement.setTextContent(path); tim@724: } tim@828: }finally{ tim@828: ExclusiveExec.INSTANCE.release(key); tim@655: } tim@655: } tim@655: tim@655: return document; tim@655: } sascha@778: tim@828: /** tim@828: * Returns the parameterType for the Layer. tim@828: * @param geometryType tim@828: * @return tim@828: */ tim@828: private String findParameterTitle(String geometryType) { tim@828: String paramType = LAYER_MODEL+"_"+templateID; tim@828: tim@828: if (!MapfileGenerator.getInstance().templateExists(paramType)){ tim@828: // If the template doesn't exist the Defaulttemplates will be used. tim@828: paramType = LAYER_MODEL+"_"+ tim@828: this.determineDefaultTemplateName(geometryType); tim@828: } tim@828: return paramType; tim@828: } tim@828: tim@828: /** tim@828: * Find the title for a wms layer specified by the user. tim@828: * tim@828: * @param inputData A collection with InputData objects. tim@828: * @return the title. tim@828: */ tim@828: protected String getLayerTitle(Collection inputData) { tim@828: for (InputData data: inputData) { tim@828: String name = data.getName(); tim@828: if (name != null && name.equals("title")) { tim@828: return (String) data.getValue(); tim@828: } tim@828: } tim@828: return null; tim@828: } tim@828: ingo@813: ingo@813: /** ingo@813: * Turns the geometry type into a form used for the templating mechanism ingo@813: * while mapfile generation. ingo@813: * ingo@813: * @param geometryType The original geometry type. ingo@813: * @return a valid geometry type fpr the template mechanism. ingo@813: */ tim@799: private String determineGeometryType(String geometryType){ sascha@778: tim@799: String returnValue = geometryType.toLowerCase(); sascha@778: tim@655: if (returnValue.equalsIgnoreCase("linestring")){ tim@655: returnValue = "Line"; tim@799: }else if (returnValue.equalsIgnoreCase("multilinestring")){ tim@799: returnValue ="Line"; tim@799: }else if (returnValue.equalsIgnoreCase("multipolygon")){ tim@799: returnValue = "Polygon"; tim@799: } tim@799: return returnValue; tim@799: } sascha@803: ingo@813: ingo@813: /** ingo@813: * Determine the default template name if no special template is existing ingo@813: * for this layer. ingo@813: * ingo@813: * @param geometryType The geometry type. ingo@813: * @return a default geometry fitting to the original geometry type. ingo@813: */ tim@799: private String determineDefaultTemplateName(String geometryType){ tim@799: tim@799: String returnValue = geometryType.toLowerCase(); tim@799: tim@799: if (returnValue.equalsIgnoreCase("multilinestring")){ tim@799: returnValue ="linestring"; tim@799: }else if (returnValue.equalsIgnoreCase("multipolygon")){ tim@799: returnValue = "polygon"; tim@799: }else if (returnValue.equalsIgnoreCase("multipoint")){ tim@799: returnValue = "point"; tim@655: } tim@655: return returnValue; tim@655: } tim@616: } ingo@813: // vim:set ts=4 sw=4 si et sta sts=4 fenc=utf8 :