/* * @(#) ShpConnector.java version 1.0 12/6/2013 * * Copyright (C) 2013 Institute for the Management of Information Systems, Athena RC, Greece. * * This library is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This library 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. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package eu.geoknow.athenarc.triplegeo.shape; import com.hp.hpl.jena.datatypes.xsd.XSDDatatype; import com.hp.hpl.jena.rdf.model.Literal; import com.hp.hpl.jena.rdf.model.Model; import com.hp.hpl.jena.rdf.model.Property; import com.hp.hpl.jena.rdf.model.RDFWriter; import com.hp.hpl.jena.rdf.model.Resource; import com.hp.hpl.jena.tdb.TDBFactory; import com.hp.hpl.jena.vocabulary.RDF; import com.hp.hpl.jena.vocabulary.RDFS; //import com.vividsolutions.jts.geom.Coordinate; import com.vividsolutions.jts.geom.Geometry; //import com.vividsolutions.jts.geom.LineString; import com.vividsolutions.jts.geom.MultiLineString; import com.vividsolutions.jts.geom.MultiPolygon; import com.vividsolutions.jts.geom.Point; //import com.vividsolutions.jts.geom.Polygon; import eu.geoknow.athenarc.triplegeo.Constants; import eu.geoknow.athenarc.triplegeo.utils.Configuration; import eu.geoknow.athenarc.triplegeo.utils.UtilsConstants; import eu.geoknow.athenarc.triplegeo.utils.UtilsLib; import java.io.File; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.net.MalformedURLException; import java.net.URLEncoder; import java.util.HashMap; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; import org.geotools.data.DataStore; import org.geotools.data.DataStoreFinder; import org.geotools.data.FeatureSource; import org.geotools.factory.Hints; import org.geotools.feature.FeatureCollection; import org.geotools.feature.FeatureIterator; import org.geotools.feature.simple.SimpleFeatureImpl; import org.geotools.referencing.CRS; import org.geotools.referencing.ReferencingFactoryFinder; //import org.opengis.geometry.MismatchedDimensionException; //import org.opengis.referencing.FactoryException; //import org.opengis.referencing.NoSuchAuthorityCodeException; import org.opengis.referencing.crs.CRSAuthorityFactory; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; //import org.opengis.referencing.operation.TransformException; import org.geotools.geometry.jts.JTS; /** * Class to convert shapefiles to RDF. * * @author jonbaraq * initially implemented for geometry2rdf utility (source: https://github.com/boricles/geometry2rdf/tree/master/Geometry2RDF) * Modified by: Kostas Patroumpas, 8/2/2013 * Modified: 6/3/2013, added support for transformation from a given CRS to WGS84 * Modified: 15/3/2013, added support for exporting custom geometries to (1) Virtuoso RDF and (2) according to WGS84 Geopositioning RDF vocabulary * Last modified by: Kostas Patroumpas, 12/6/2013 */ public class ShpConnector { private static final Logger LOG = Logger.getLogger(ShpConnector.class.getName()); private static final String STRING_TO_REPLACE = "+"; private static final String REPLACEMENT = "%20"; private static final String SEPARATOR = "_"; private static final String BLANK = " "; private Model model; private Configuration configuration; private FeatureCollection featureCollection; private MathTransform transform = null; public ShpConnector(Configuration configuration) throws IOException { this.configuration = configuration; model = getModelFromConfiguration(configuration); featureCollection = getShapeFileFeatureCollection(configuration.inputFile, configuration.featureString); System.setProperty("org.geotools.referencing.forceXY", "true"); } /** * Loads the shape file from the configuration path and returns the * feature collection associated according to the configuration. * * @param shapePath with the path to the shapefile. * @param featureString with the featureString to filter. * * @return FeatureCollection with the collection of features filtered. */ private FeatureCollection getShapeFileFeatureCollection(String shapePath, String featureString) throws IOException { File file = new File(shapePath); // Create the map with the file URL to be passed to DataStore. Map map = new HashMap(); try { map.put("url", file.toURL()); } catch (MalformedURLException ex) { Logger.getLogger(ShpConnector.class.getName()).log(Level.SEVERE, null, ex); } if (map.size() > 0) { DataStore dataStore = DataStoreFinder.getDataStore(map); FeatureSource featureSource = dataStore.getFeatureSource(featureString); return featureSource.getFeatures(); } return null; } /** * Returns a Jena RDF model populated with the params from the configuration. * * @param configuration with all the configuration parameters. * * @return a Jena RDF model populated with the params from the configuration. */ private Model getModelFromConfiguration(Configuration configuration) { UtilsLib.removeDirectory(configuration.tmpDir); Model tmpModel = TDBFactory.createModel(configuration.tmpDir); tmpModel.removeAll(); tmpModel.setNsPrefix("geontology", configuration.ontologyNS); tmpModel.setNsPrefix("georesource", configuration.nsUri); tmpModel.setNsPrefix("geo", URLConstants.NS_GEO); tmpModel.setNsPrefix("sf", URLConstants.NS_SF); tmpModel.setNsPrefix("dc", URLConstants.NS_DC); tmpModel.setNsPrefix("xsd", URLConstants.NS_XSD); //Check if a coordinate transform should be made if (configuration.sourceCRS != null) try { boolean lenient = true; // allow for some error due to different datums Hints hints = new Hints(Hints.FORCE_LONGITUDE_FIRST_AXIS_ORDER, Boolean.TRUE); CRSAuthorityFactory factory = ReferencingFactoryFinder.getCRSAuthorityFactory("EPSG", hints); CoordinateReferenceSystem sourceCRS = factory.createCoordinateReferenceSystem(configuration.sourceCRS); CoordinateReferenceSystem targetCRS = factory.createCoordinateReferenceSystem(configuration.targetCRS); transform = CRS.findMathTransform(sourceCRS, targetCRS, lenient); System.out.println("Transformation will take place from " + configuration.sourceCRS + " to " + configuration.targetCRS + " reference system."); } catch (Exception e) { e.printStackTrace(); } else System.out.println("No transformation will take place. Input data is expected in WGS84 reference system."); return tmpModel; } /** * * Handling non-spatial attributes only **/ public void handleNonGeometricAttributes(SimpleFeatureImpl feature) throws UnsupportedEncodingException, FileNotFoundException { try { String featureAttribute = "featureWithoutID"; String featureName = "featureWithoutName"; String featureClass = "featureWithoutClass"; //Feature id if (feature.getAttribute(configuration.attribute) != null) { featureAttribute = feature.getAttribute(configuration.attribute).toString(); } //Feature name if (feature.getAttribute(configuration.featname) != null) { featureName = feature.getAttribute(configuration.featname).toString(); } //Feature classification if (feature.getAttribute(configuration.featclass) != null) { featureClass = feature.getAttribute(configuration.featclass).toString(); } if (!featureAttribute.equals(configuration.ignore)) { String encodingType = URLEncoder.encode(configuration.type, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); String encodingResource = URLEncoder.encode(featureAttribute, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); String aux = encodingType + SEPARATOR + encodingResource; //Insert literal for name (name of feature) if ((!featureName.equals(configuration.ignore)) && (!featureName.equals(""))) //NOT NULL values only { insertResourceNameLiteral( configuration.nsUri + aux, configuration.nsUri + Constants.NAME, featureName, configuration.defaultLang ); } //Insert literal for class (type of feature) if ((!featureClass.equals(configuration.ignore)) && (!featureClass.equals(""))) //NOT NULL values only { encodingResource = URLEncoder.encode(featureClass, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); //Type according to application schema insertResourceTypeResource( configuration.nsUri + aux, configuration.nsUri + encodingResource); } } } catch(Exception e) {} } /** * * Writes the RDF model into a file */ public void writeRdfModel() throws UnsupportedEncodingException, FileNotFoundException { FeatureIterator iterator = featureCollection.features(); int position = 0; long t_start = System.currentTimeMillis(); long dt = 0; try { System.out.println(UtilsLib.getGMTime() + " Started processing features..."); while(iterator.hasNext()) { SimpleFeatureImpl feature = (SimpleFeatureImpl) iterator.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); //Attempt to transform geometry into the target CRS if (transform != null) try { geometry = JTS.transform(geometry, transform); } catch (Exception e) { e.printStackTrace(); } String featureAttribute = "featureWithoutID"; //Process non-spatial attributes for name and type handleNonGeometricAttributes(feature); if (feature.getAttribute(configuration.attribute) != null) { featureAttribute = feature.getAttribute(configuration.attribute).toString(); } if (!featureAttribute.equals(configuration.ignore)) { String encodingType = URLEncoder.encode(configuration.type, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); String encodingResource = URLEncoder.encode(featureAttribute, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); String aux = encodingType + SEPARATOR + encodingResource; //Type according to application schema insertResourceTypeResource( configuration.nsUri + aux, configuration.nsUri + URLEncoder.encode( configuration.type, UtilsConstants.UTF_8).replace( STRING_TO_REPLACE, REPLACEMENT)); //Type according to GeoSPARQL feature insertResourceTypeResource( configuration.nsUri + aux, configuration.ontologyNS + Constants.FEATURE ); insertLabelResource(configuration.nsUri + aux, featureAttribute, configuration.defaultLang); if (geometry.getGeometryType().equals(Constants.POINT)) { insertPoint(aux, geometry); } else if (geometry.getGeometryType().equals(Constants.LINE_STRING)) { insertLineString(aux, geometry); } else if (geometry.getGeometryType().equals(Constants.POLYGON)) { insertPolygon(aux, geometry); } else if (geometry.getGeometryType().equals(Constants.MULTI_POLYGON)) { MultiPolygon multiPolygon = (MultiPolygon) geometry; for (int i = 0; i < multiPolygon.getNumGeometries(); ++i) { Geometry tmpGeometry = multiPolygon.getGeometryN(i); if (tmpGeometry.getGeometryType().equals(Constants.POLYGON)) { insertPolygon(aux, tmpGeometry); } else if (tmpGeometry.getGeometryType().equals(Constants.LINE_STRING)) { insertLineString(aux, tmpGeometry); } } } else if (geometry.getGeometryType().equals(Constants.MULTI_LINE_STRING)) { MultiLineString multiLineString = (MultiLineString) geometry; for (int i = 0; i < multiLineString.getNumGeometries(); ++i) { Geometry tmpGeometry = multiLineString.getGeometryN(i); if (tmpGeometry.getGeometryType().equals(Constants.POLYGON)) { insertPolygon(aux, tmpGeometry); } else if (tmpGeometry.getGeometryType().equals(Constants.LINE_STRING)) { insertLineString(aux, tmpGeometry); } } } } else { LOG.log(Level.INFO, "writeRdfModel: Not processing feature attribute in position {0}", position); } if (position%1000 ==0) System.out.print(UtilsLib.getGMTime() + " Processed " + position + " records..." + "\r"); ++position; } } finally { iterator.close(); } dt = System.currentTimeMillis() - t_start; System.out.println(UtilsLib.getGMTime() + " Parsing completed for " + position + " records in " + dt + " ms."); System.out.println(UtilsLib.getGMTime() + " Starting to write triplets to file..."); //Count the number of statements long numStmt = model.listStatements().toSet().size(); //Export model to a suitable format FileOutputStream out = new FileOutputStream(configuration.outputFile); model.write(out, configuration.outFormat); dt = System.currentTimeMillis() - t_start; System.out.println(UtilsLib.getGMTime() + " Process concluded in " + dt + " ms. " + numStmt + " triples successfully exported to " + configuration.outFormat + " file: " + configuration.outputFile + "."); } /** * Export POINT geometries according to Virtuoso RDF specs * * @param specs should indicate either 'Virtuoso' or 'wgs84_pos' */ public void writePointRdfModel(String specs) throws UnsupportedEncodingException, FileNotFoundException { FeatureIterator iterator = featureCollection.features(); int position = 0; long t_start = System.currentTimeMillis(); long dt = 0; try { System.out.println(UtilsLib.getGMTime() + " Started processing features..."); while(iterator.hasNext()) { SimpleFeatureImpl feature = (SimpleFeatureImpl) iterator.next(); Geometry geometry = (Geometry) feature.getDefaultGeometry(); //Attempt to transform geometry into the target CRS if (transform != null) try { geometry = JTS.transform(geometry, transform); } catch (Exception e) { e.printStackTrace(); } String featureAttribute = "featureWithoutID"; //Process non-spatial attributes for name and type handleNonGeometricAttributes(feature); if (feature.getAttribute(configuration.attribute) != null) { featureAttribute = feature.getAttribute(configuration.attribute).toString(); } if (!featureAttribute.equals(configuration.ignore)) { String encodingType = URLEncoder.encode(configuration.type, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); String encodingResource = URLEncoder.encode(featureAttribute, UtilsConstants.UTF_8).replace(STRING_TO_REPLACE, REPLACEMENT); String aux = encodingType + SEPARATOR + encodingResource; //Type according to application schema insertResourceTypeResource( configuration.nsUri + aux, configuration.nsUri + URLEncoder.encode( configuration.type, UtilsConstants.UTF_8).replace( STRING_TO_REPLACE, REPLACEMENT)); //Label using the given attribute (usually an id) insertLabelResource(configuration.nsUri + aux, featureAttribute, configuration.defaultLang); //Point geometries ONLY if (geometry.getGeometryType().equals(Constants.POINT)) { if (specs.equals("Virtuoso")) insertVirtuosoPoint(aux, geometry); else if (specs.equals("wgs84_pos")) insertWGS84Point(aux, geometry); } } else { LOG.log(Level.INFO, "writeRdfModel: Not processing feature attribute in position {0}", position); } if (position%1000 ==0) System.out.print(UtilsLib.getGMTime() + " Processed " + position + " records..." + "\r"); ++position; } } finally { iterator.close(); } dt = System.currentTimeMillis() - t_start; System.out.println(UtilsLib.getGMTime() + " Parsing completed for " + position + " records in " + dt + " ms."); System.out.println(UtilsLib.getGMTime() + " Starting to write triplets to file..."); //Count the number of statements long numStmt = model.listStatements().toSet().size(); //Export model to a suitable format FileOutputStream out = new FileOutputStream(configuration.outputFile); model.write(out, configuration.outFormat); dt = System.currentTimeMillis() - t_start; System.out.println(UtilsLib.getGMTime() + " Process concluded in " + dt + " ms. " + numStmt + " triples successfully exported to " + configuration.outFormat + " file: " + configuration.outputFile + "."); } // /** * Handle Polyline geometry according to GeoSPARQL standard * */ private void insertLineString(String resource, Geometry geo) { insertResourceTriplet(configuration.nsUri + resource, URLConstants.NS_GEO + "hasGeometry", configuration.nsUri + UtilsConstants.FEAT + resource); insertResourceTypeResource(configuration.nsUri + UtilsConstants.FEAT + resource, URLConstants.NS_SF + Constants.LINE_STRING); insertLiteralTriplet( configuration.nsUri + UtilsConstants.FEAT + resource, URLConstants.NS_GEO + Constants.WKT, geo.toText(), URLConstants.NS_GEO + Constants.WKTLiteral ); } /** * * Handle Polygon geometry according to GeoSPARQL standard */ private void insertPolygon(String resource, Geometry geo) throws UnsupportedEncodingException { insertResourceTriplet(configuration.nsUri + resource, URLConstants.NS_GEO + "hasGeometry", configuration.nsUri + UtilsConstants.FEAT + resource); insertResourceTypeResource(configuration.nsUri + UtilsConstants.FEAT + resource, URLConstants.NS_SF + Constants.POLYGON); insertLiteralTriplet( configuration.nsUri + UtilsConstants.FEAT + resource, URLConstants.NS_GEO + Constants.WKT, geo.toText(), URLConstants.NS_GEO + Constants.WKTLiteral); } /** * * Handle resource type */ private void insertResourceTypeResource(String r1, String r2) { Resource resource1 = model.createResource(r1); Resource resource2 = model.createResource(r2); model.add(resource1, RDF.type, resource2); } /** * * Handle triples for string literals */ private void insertLiteralTriplet(String s, String p, String o, String x) { Resource resourceGeometry = model.createResource(s); Property property = model.createProperty(p); if (x != null) { Literal literal = model.createTypedLiteral(o, x); resourceGeometry.addLiteral(property, literal); } else { resourceGeometry.addProperty(property, o); } } /** * * Handle string literals for 'name' attribute */ private void insertResourceNameLiteral(String s, String p, String o, String lang) { Resource resource = model.createResource(s); Property property = model.createProperty(p); if (lang != null) { Literal literal = model.createLiteral(o, lang); resource.addLiteral(property, literal); } else { resource.addProperty(property, o); } } /** * * Handle resource triples */ private void insertResourceTriplet(String s, String p, String o) { Resource resourceGeometry = model.createResource(s); Property property = model.createProperty(p); Resource resourceGeometry2 = model.createResource(o); resourceGeometry.addProperty(property, resourceGeometry2); } /** * * Handle label triples */ private void insertLabelResource(String resource, String label, String lang) { Resource resource1 = model.createResource(resource); model.add(resource1, RDFS.label, model.createLiteral(label, lang)); } /** * * Point geometry according to GeoSPARQL standard */ private void insertPoint(String resource, Geometry geo) { insertResourceTriplet( configuration.nsUri + resource, URLConstants.NS_GEO + "hasGeometry", configuration.nsUri + UtilsConstants.FEAT + resource); insertResourceTypeResource( configuration.nsUri + UtilsConstants.FEAT + resource, URLConstants.NS_SF + Constants.POINT); insertLiteralTriplet( configuration.nsUri + UtilsConstants.FEAT + resource, URLConstants.NS_GEO + Constants.WKT, geo.toText(), URLConstants.NS_GEO + Constants.WKTLiteral ); } /** * * Point geometry according to Virtuoso RDF specifics */ private void insertVirtuosoPoint(String resource, Geometry geo) { insertLiteralTriplet( configuration.nsUri + resource, Constants.NSPOS + Constants.GEOMETRY, geo.toText(), Constants.NSVIRT + Constants.GEOMETRY ); } /** * * Point geometry according to WGS84 Geoposition RDF vocabulary */ private void insertWGS84Point(String resource, Geometry geo) { Point p = (Point) geo; insertLiteralTriplet( configuration.nsUri + resource, Constants.NSPOS + Constants.LONGITUDE, String.valueOf(p.getX()), //X-ordinate as a property Constants.NSXSD + "decimal" ); insertLiteralTriplet( configuration.nsUri + resource, Constants.NSPOS + Constants.LATITUDE, String.valueOf(p.getY()), //Y-ordinate as a property Constants.NSXSD + "decimal" ); } }