/** * Copyright (C) 2007 - 2016 52°North Initiative for Geospatial Open Source * Software GmbH * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 as published * by the Free Software Foundation. * * If the program is linked with libraries which are licensed under one of * the following licenses, the combination of the program with the linked * library is not considered a "derivative work" of the program: * * • Apache License, version 2.0 * • Apache Software License, version 1.0 * • GNU Lesser General Public License, version 3 * • Mozilla Public License, versions 1.0, 1.1 and 2.0 * • Common Development and Distribution License (CDDL), version 1.0 * * Therefore the distribution of the program linked with libraries licensed * under the aforementioned licenses, is permitted by the copyright holders * if the distribution is compliant with both the GNU General Public * License version 2 and the aforementioned licenses. * * As an exception to the terms of the GPL, you may copy, modify, * propagate, and distribute a work formed by combining 52°North WPS * GeoTools Modules with the Eclipse Libraries, or a work derivative of * such a combination, even if such copying, modification, propagation, or * distribution would otherwise violate the terms of the GPL. Nothing in * this exception exempts you from complying with the GPL in all respects * for all of the code used other than the Eclipse Libraries. You may * include this exception and its grant of permissions when you distribute * 52°North WPS GeoTools Modules. Inclusion of this notice with such a * distribution constitutes a grant of such permissions. If you do not wish * to grant these permissions, remove this paragraph from your * distribution. "52°North WPS GeoTools Modules" means the 52°North WPS * modules using GeoTools functionality - software licensed under version 2 * or any later version of the GPL, or a work based on such software and * licensed under the GPL. "Eclipse Libraries" means Eclipse Modeling * Framework Project and XML Schema Definition software distributed by the * Eclipse Foundation and licensed under the Eclipse Public License Version * 1.0 ("EPL"), or a work based on such software and licensed under the EPL. * * This program is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General * Public License for more details. */ package org.n52.wps.io.datahandler.generator.mapserver; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.io.Serializable; import java.net.URISyntaxException; import java.net.URL; import java.nio.channels.FileChannel; import java.util.HashMap; import java.util.Map; import org.geotools.data.DefaultTransaction; import org.geotools.data.FeatureStore; import org.geotools.data.Transaction; import org.geotools.data.shapefile.ShapefileDataStore; import org.geotools.data.shapefile.ShapefileDataStoreFactory; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.data.simple.SimpleFeatureStore; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import edu.umn.gis.mapscript.classObj; import edu.umn.gis.mapscript.layerObj; import edu.umn.gis.mapscript.mapObj; import edu.umn.gis.mapscript.styleObj; /** * This class managed the modifying of the mapfile of an UMN Mapserver. Right * now it only supports the adding of a Geotools FeatureCollection. * * @author Jacob Mendt * * @TODO better solution for replacing the old with the new mapfile. * @TODO Giving the ShapefileRepository as an external Input in the method * addFeatureCollectionToMapfile is maybe not the best way. * @TODO check if layer is already in mapfile. * */ public final class MSMapfileBinding { // thread-safe Singleton-Patterns private static MSMapfileBinding instance = new MSMapfileBinding(); private static Logger LOGGER = LoggerFactory.getLogger(MSMapfileBinding.class); /** * Default initialization of a new MSMapfileBinding Object. */ private MSMapfileBinding() { } /** * Implementation of a thread-safe Singleton Pattern * * @return MSMapfileBinding.class */ public static synchronized MSMapfileBinding getInstance() { return instance; } /** * This method adds a FeatureCollection as an extra layer to the default * mapfile of this MSMapfileBinding object. * * @param ftColl * Input FeatureCollection which should be added as a layer to * the mapfile. * @param workspace * Path to the workspace folder of the mapfile object. * @param mapfile * Name of the mapfile object in the workspace folder. * @param dataRep * Path to the data repository of the mapserver. * * @return The name of the layer of featureCollection which was added to the * mapfile. */ public synchronized String addFeatureCollectionToMapfile( SimpleFeatureCollection ftColl, String workspacePath, String mapfilePath, String dataRepPath) { // saves the featureCollection as a shapefile in the dataRepository of // the mapfile String shapefilePath = null; try { shapefilePath = this.saveFeatureCollectionToShapefile(ftColl, workspacePath, dataRepPath); LOGGER.debug("Saving FeatureCollection as a Shapefile to the data repository of the mapfile successful."); } catch (IOException e1) { LOGGER.error("Error while saving FeatureCollection as a Shapefile to the data repository of the mapfile."); e1.printStackTrace(); } catch (URISyntaxException e1) { LOGGER.error("Error while saving FeatureCollection as a Shapefile to the data repository of the mapfile."); e1.printStackTrace(); } catch (Exception e) { LOGGER.error("Error while saving FeatureCollection as a Shapefile to the data repository of the mapfile."); e.printStackTrace(); } // makes a temporary copy of the mapfile String orgMapfilePath = workspacePath + mapfilePath; String tmpMapfilePath = workspacePath + System.currentTimeMillis() + ".map"; try { if (copyMapfile(orgMapfilePath, tmpMapfilePath)) { LOGGER.debug("Creating of a temporary copy of the mapfile successful."); } else { LOGGER.error("Error while creating a temporary copy of the mapfile."); return null; } } catch (IOException e) { LOGGER.error("Error while creating a temporary copy of the mapfile."); e.printStackTrace(); } // adds a the shapefile as a layer to the mapfile String layerName = null; try { layerName = this.addShapefileAsLayerToMapfile(shapefilePath, tmpMapfilePath, workspacePath); LOGGER.debug("Adding shapefile as a layer to the mapfile successful."); } catch (Exception e) { LOGGER.error("Error while adding shapefile as a layer to the mapfile."); e.printStackTrace(); } // replace original mapfile through the temporary mapfile try { if (this.deleteMapfile(orgMapfilePath)) { if (this.copyMapfile(tmpMapfilePath, orgMapfilePath)) { this.deleteMapfile(tmpMapfilePath); LOGGER.info("Adding a FeatureCollection as a layer to the mapfile successful."); return layerName; } } } catch (Exception e) { LOGGER.error("Error while updating the original mapfile"); e.printStackTrace(); } // default return return null; } /** * This method adds a shapfile as an extra layer to the default mapfile of * this MSMapfileBinding object. * * @param shapefilePath * Path to the shapefile which should be added to the mapfile. * @param tmpMapfilePath * Path to the temporary copy of the mapfile. * @param workspacePath * to the workspace folder of the mapfile object * * @return The name of the added layer. * * @TODO Check if layer already exist in the mapfile. */ private synchronized String addShapefileAsLayerToMapfile( String shapefilePath, String tmpMapfilePath, String workspacePath) { // initalize mapfile object mapObj mapfileObj = new mapObj(tmpMapfilePath); try { // initialize a BindingClass between the shapefile and mapscript // objects MSLayerBinding msLayer = null; try { msLayer = new MSLayerBinding(shapefilePath, workspacePath); LOGGER.debug("Initialzing MSLayerBinding object successful."); } catch (Exception e) { LOGGER.error("Error while initializing MSLayerBinding object."); e.printStackTrace(); } if (msLayer != null) { // initalize a new mapfile layer object layerObj layer = new layerObj(mapfileObj); /* * setting the propertys of the layer */ layer.setData(msLayer.getDataSourcePath()); layer.setDump(1); // True -> Switch to allow MapServer to return // data in GML format layer.setName(msLayer.getMdTitle()); layer.setProjection(msLayer.getMdCRS()); layer.setStatus(1); // means ON - Sets the current status of the // layer. Often modified by MapServer // itself. Default turns the layer on // permanently. layer.setType(msLayer.getGeometryType()); // set metadata layer.setMetaData("wms_title", msLayer.getMdTitle()); layer.setMetaData("wms_timestamp", msLayer.getMdTimestamp()); // only date of the timestamp when the layer was added to the mapfile /* * create a class and a style object of the layer and set styles * and properties */ classObj layClass = new classObj(layer); layClass.setName(msLayer.getMdTitle()); styleObj layStyle = new styleObj(layClass); layStyle.setColor(MSColorStyles.getDefaultColor()); layStyle.setOutlinecolor(MSColorStyles.getDefaultOutlineColor()); /* * checks if the layerCRS is already supported by the wms, if * not adds the layer CRS to the global metadata tag "wms_srs" */ String wmsSupportedCRS = mapfileObj.getMetaData("wms_srs"); mapfileObj.setMetaData( "wms_srs", this.getMDSupportedCRS(wmsSupportedCRS, msLayer.getMdCRS())); // saves the new layer mapfileObj.save(tmpMapfilePath); LOGGER.debug("Setting layer data successful."); return msLayer.getMdTitle(); } } catch (Exception e) { LOGGER.error("Error while adding shapefile to mapfile."); e.printStackTrace(); } finally { mapfileObj = null; } // default return return null; } /** * Checks if the metadata tag "wms_srs" already contains the crs of the * layer * * @param wmsSupportedCrs * content of the metadata tag "wms_srs" * @param mdCrs * epsg code of the layer * @return */ private String getMDSupportedCRS(String wmsSupportedCrs, String mdCrs) { String[] aCrs = wmsSupportedCrs.split(" "); for (int i = 0; i < aCrs.length; i++) { if (aCrs[i].equalsIgnoreCase(mdCrs)) { return wmsSupportedCrs; } } wmsSupportedCrs = wmsSupportedCrs + " " + mdCrs; return wmsSupportedCrs; } /** * Creates a copy of the mapfile * * @param String * orgMapfilePath Path to the original mapfile. * @param String * tmpMapfilePath Path to the temporary mapfile. * * @return <tt>true</tt> if the copying of the mapfile went okay. * * @throws IOException * If something went wrong while copying the content of the * original mapfile to the temporary mapfile. */ private synchronized boolean copyMapfile(String orgMapfilePath, String tmpMapfilePath) throws IOException { // does a tmp copy of the mapfile File orgMapfile = new File(orgMapfilePath); File tmpMapfile = new File(tmpMapfilePath); FileChannel inCh = new FileInputStream(orgMapfile).getChannel(); FileChannel outCh = new FileOutputStream(tmpMapfile).getChannel(); try { inCh.transferTo(0, inCh.size(), outCh); LOGGER.debug("Creating of an temporary copy of the mapfile successful."); return true; } catch (IOException e) { LOGGER.error("Error while copying mapfile"); e.printStackTrace(); } finally { if (inCh != null) inCh.close(); if (outCh != null) outCh.close(); } // default return return false; } /** * Deletes the mapfile. * * @param mapfilePath * Path to the mapfile which should be delete. * * @return <tt>true</tt> if the mapfile was deleted successful. */ private boolean deleteMapfile(String mapfilePath) { File tmpFile = new File(mapfilePath); if (tmpFile.exists()) { tmpFile.delete(); return true; } else { LOGGER.debug("Mapfile doesn't exist."); return false; } } /** * Saves the FeatureCollection as a shapefile in the data repository of the * mapserver. If the CRS of the FeatureCollection is not WGS84 the * FeatureCollection will be transformed to WGS 84 * * @param ftColl * FeatureCollection which should be saved as shapefile * @param workspacePath * Path to the workspace folder of the mapfile object. * @param dataRepository * Path to the data repository of the mapserver. * * @return Path to the shapefile, which lies in the data repository of the * mapserver. * * @throws IOException * @throws URISyntaxException * @throws Exception */ private String saveFeatureCollectionToShapefile( SimpleFeatureCollection ftColl, String workspacePath, String dataRepository) throws IOException, URISyntaxException, Exception { //FeatureCollection ftColl = createCorrectFeatureCollection(ftCollWrong); //FeatureCollectionTester tester = new FeatureCollectionTester(); //tester.testFeatureCollection(ftColl); SimpleFeatureType TYPE = (SimpleFeatureType) ftColl.getSchema(); String shapefilePath; // Create the shapefilePath if (dataRepository.endsWith("/") || dataRepository.endsWith("\\")) { shapefilePath = workspacePath + dataRepository + "shape_" + TYPE.getName().getLocalPart().toLowerCase() + "_" + System.currentTimeMillis() + ".shp"; } else shapefilePath = workspacePath + dataRepository + "/shape_" + TYPE.getName().getLocalPart().toLowerCase() + "_" + System.currentTimeMillis() + ".shp"; /* * Get an output file name and create the new shapefile */ File shapefile = new File(shapefilePath); URL url = shapefile.toURI().toURL(); ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory(); Map<String, Serializable> params = new HashMap<String, Serializable>(); params.put("url", url); params.put("create spatial index", Boolean.TRUE); ShapefileDataStore newDataStore = (ShapefileDataStore) dataStoreFactory .createNewDataStore(params); // Initialize the CRS for transformation CoordinateReferenceSystem sourceCrs = TYPE .getCoordinateReferenceSystem(); newDataStore.createSchema(TYPE); newDataStore.forceSchemaCRS(sourceCrs); /* * Write the features to the shapefile */ Transaction transaction = new DefaultTransaction("create"); String typeName = newDataStore.getTypeNames()[0]; SimpleFeatureSource featureSource = newDataStore.getFeatureSource(typeName); if (featureSource instanceof FeatureStore) { SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource; featureStore.setTransaction(transaction); try { featureStore.addFeatures(ftColl); transaction.commit(); LOGGER.debug("Saving FeatureCollection as shapefile successful."); } catch (Exception problem) { LOGGER.error("Error while saving FeatureCollection as shapefile."); problem.printStackTrace(); transaction.rollback(); } finally { transaction.close(); } } else { LOGGER.error(typeName + " does not support read/write access"); return null; } // returns the path to the shapefile return url.getPath(); } }