| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| package org.apache.jena.geosparql.configuration; |
| |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.invoke.MethodHandles; |
| import java.util.ArrayList; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.TreeSet; |
| import java.util.UUID; |
| import org.apache.jena.datatypes.DatatypeFormatException; |
| import org.apache.jena.datatypes.RDFDatatype; |
| import org.apache.jena.geosparql.implementation.GeometryWrapper; |
| import org.apache.jena.geosparql.implementation.datatype.GMLDatatype; |
| import org.apache.jena.geosparql.implementation.datatype.GeometryDatatype; |
| import org.apache.jena.geosparql.implementation.datatype.WKTDatatype; |
| import org.apache.jena.geosparql.implementation.index.GeometryLiteralIndex; |
| import org.apache.jena.geosparql.implementation.vocabulary.Geo; |
| import org.apache.jena.geosparql.implementation.vocabulary.GeoSPARQL_URI; |
| import org.apache.jena.geosparql.implementation.vocabulary.SpatialExtension; |
| import org.apache.jena.geosparql.spatial.ConvertLatLon; |
| import org.apache.jena.query.Dataset; |
| import org.apache.jena.query.DatasetFactory; |
| import org.apache.jena.query.ReadWrite; |
| import org.apache.jena.rdf.model.InfModel; |
| import org.apache.jena.rdf.model.Literal; |
| import org.apache.jena.rdf.model.Model; |
| import org.apache.jena.rdf.model.ModelFactory; |
| import org.apache.jena.rdf.model.NodeIterator; |
| import org.apache.jena.rdf.model.Property; |
| import org.apache.jena.rdf.model.RDFNode; |
| import org.apache.jena.rdf.model.ResIterator; |
| import org.apache.jena.rdf.model.Resource; |
| import org.apache.jena.rdf.model.ResourceFactory; |
| import org.apache.jena.rdf.model.Statement; |
| import org.apache.jena.rdf.model.StmtIterator; |
| import org.apache.jena.reasoner.Reasoner; |
| import org.apache.jena.reasoner.ReasonerRegistry; |
| import org.apache.jena.riot.Lang; |
| import org.apache.jena.riot.RDFDataMgr; |
| import org.apache.jena.util.iterator.ExtendedIterator; |
| import org.apache.jena.vocabulary.RDFS; |
| import org.opengis.geometry.MismatchedDimensionException; |
| import org.opengis.referencing.operation.TransformException; |
| import org.opengis.util.FactoryException; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * |
| * |
| */ |
| public class GeoSPARQLOperations { |
| |
| private static final String GEOSPARQL_SCHEMA_FILE = "schema/geosparql_vocab_all_v1_0_1_updated.rdf"; |
| private static final Logger LOGGER = LoggerFactory.getLogger(MethodHandles.lookup().lookupClass()); |
| |
| /** |
| * Every subProperty of hasGeometry is made a subProperty of |
| * hasDefaultGeometry.<br> |
| * Assumption that each Feature has a single hasGeometry property.<br> |
| * Requires RDFS inferencing to propagate through the data. |
| * |
| * @param model |
| */ |
| public static final void applySubPropertyDefaultGeometry(Model model) { |
| |
| try { |
| ResIterator resIt = model.listResourcesWithProperty(RDFS.subPropertyOf, Geo.HAS_GEOMETRY_PROP); |
| while (resIt.hasNext()) { |
| Resource res = resIt.nextResource(); |
| res.addProperty(RDFS.subPropertyOf, Geo.HAS_DEFAULT_GEOMETRY_PROP); |
| } |
| |
| } catch (Exception ex) { |
| LOGGER.error("Inserting GeoSPARQL predicates error: {}", ex.getMessage()); |
| } |
| } |
| |
| /** |
| * Apply hasDefaultGeometry for every Feature with a single hasGeometry |
| * property. |
| * |
| * @param dataset |
| */ |
| public static final void applyDefaultGeometry(Dataset dataset) { |
| |
| try { |
| LOGGER.info("Applying hasDefaultGeometry - Started"); |
| //Default Model |
| dataset.begin(ReadWrite.WRITE); |
| Model defaultModel = dataset.getDefaultModel(); |
| GeoSPARQLOperations.applyDefaultGeometry(defaultModel); |
| |
| //Named Models |
| Iterator<String> graphNames = dataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| GeoSPARQLOperations.applyDefaultGeometry(namedModel); |
| } |
| |
| dataset.commit(); |
| LOGGER.info("Applying hasDefaultGeometry - Completed"); |
| } catch (Exception ex) { |
| LOGGER.error("Write Error: {}", ex.getMessage()); |
| } finally { |
| dataset.end(); |
| } |
| |
| } |
| |
| /** |
| * Apply hasDefaultGeometry for every Feature with a single hasGeometry |
| * property. |
| * |
| * @param model |
| */ |
| public static final void applyDefaultGeometry(Model model) { |
| |
| ResIterator featureIt = model.listResourcesWithProperty(Geo.HAS_GEOMETRY_PROP); |
| while (featureIt.hasNext()) { |
| Resource feature = featureIt.nextResource(); |
| if (!feature.hasProperty(Geo.HAS_DEFAULT_GEOMETRY_PROP)) { |
| List<Statement> statement = feature.listProperties(Geo.HAS_GEOMETRY_PROP).toList(); |
| if (statement.size() == 1) { |
| try { |
| Resource geometry = statement.get(0).getResource(); |
| feature.addProperty(Geo.HAS_DEFAULT_GEOMETRY_PROP, geometry); |
| } catch (Exception ex) { |
| LOGGER.error("Error creating default geometry: {}", ex.getMessage()); |
| } |
| } |
| } |
| } |
| } |
| |
| /** |
| * Apply hasDefaultGeometry for every Feature with a single hasGeometry |
| * property. |
| * |
| * @param inputFile |
| * @param inputLang |
| * @param outputFile |
| * @param outputLang |
| */ |
| public static final void applyDefaultGeometry(File inputFile, Lang inputLang, File outputFile, Lang outputLang) { |
| |
| LOGGER.info("Applying Predicates from File: {} to {} - Started", inputFile.getAbsolutePath(), outputFile.getAbsolutePath()); |
| |
| Model model = applyDefaultGeometry(inputFile, inputLang); |
| //Write the output. |
| writeOutputModel(model, outputFile, outputLang, inputFile); |
| |
| LOGGER.info("Applying Predicates from File: {} to {} - Completed", inputFile.getAbsolutePath(), outputFile.getAbsolutePath()); |
| } |
| |
| /** |
| * Apply hasDefaultGeometry for every Feature with a single hasGeometry |
| * property. |
| * |
| * @param inputFile |
| * @param inputLang |
| * @return Output model. |
| */ |
| public static final Model applyDefaultGeometry(File inputFile, Lang inputLang) { |
| Model model = ModelFactory.createDefaultModel(); |
| try (FileInputStream inputStream = new FileInputStream(inputFile)) { |
| RDFDataMgr.read(model, inputStream, inputLang); |
| applyDefaultGeometry(model); |
| } catch (IOException ex) { |
| LOGGER.error("Input File IO Exception: {} - {}", inputFile.getAbsolutePath(), ex.getMessage()); |
| } |
| |
| return model; |
| } |
| |
| /** |
| * Apply (to a folder of RDF files) hasDefaultGeometry for every Feature |
| * with a single hasGeometry property. |
| * <br> Only RDF files should be in the input folder and must all be the |
| * same RDF * language. |
| * |
| * @param inputFolder |
| * @param inputLang |
| * @param outputFolder |
| * @param outputLang |
| */ |
| public static final void applyDefaultGeometryFolder(File inputFolder, Lang inputLang, File outputFolder, Lang outputLang) { |
| |
| LOGGER.info("Applying Predicates from Folder {} to {} - Started", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath()); |
| if (inputFolder.exists()) { |
| File[] inputFiles = inputFolder.listFiles(); |
| |
| if (inputFiles.length > 0) { |
| outputFolder.mkdir(); |
| |
| for (File inputFile : inputFiles) { |
| File outputFile = new File(outputFolder, inputFile.getName()); |
| try { |
| applyDefaultGeometry(inputFile, inputLang, outputFile, outputLang); |
| } catch (Exception ex) { |
| LOGGER.error("{} for input {}. The output file {} may not be created.", ex.getMessage(), inputFile.getAbsolutePath(), outputFile.getAbsolutePath()); |
| } |
| } |
| |
| } else { |
| LOGGER.warn("{} is empty. {} is not created.", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath()); |
| } |
| |
| } else { |
| LOGGER.warn("{} does not exist. {} is not created.", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath()); |
| } |
| LOGGER.info("Applying Predicates from Folder {} to {} - Completed", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath()); |
| } |
| |
| /** |
| * Load GeoSPARQL v1.0 (corrected version) as a Model. |
| * |
| * @return Model containing the schema. |
| */ |
| public static final Model loadGeoSPARQLSchema() { |
| Model geosparqlSchema = ModelFactory.createDefaultModel(); |
| InputStream inputStream = GeoSPARQLOperations.class.getClassLoader().getResourceAsStream(GEOSPARQL_SCHEMA_FILE); |
| RDFDataMgr.read(geosparqlSchema, inputStream, Lang.RDFXML); |
| |
| return geosparqlSchema; |
| } |
| |
| /** |
| * Apply GeoSPARQL inferencing using GeoSPARPQL v1.0 (corrected version) and |
| * RDFS reasoner.<br> |
| * Statements will be added to the dataset. |
| * |
| * @param dataset |
| */ |
| public static final void applyInferencing(Dataset dataset) { |
| Model geosparqlSchema = loadGeoSPARQLSchema(); |
| applyInferencing(geosparqlSchema, dataset); |
| } |
| |
| /** |
| * Apply GeoSPARQL inferencing using schema model and RDFS reasoner.<br> |
| * Statements will be added to the Dataset. |
| * |
| * @param geosparqlSchema |
| * @param dataset |
| */ |
| public static final void applyInferencing(Model geosparqlSchema, Dataset dataset) { |
| |
| LOGGER.info("Applying GeoSPARQL Schema - Started"); |
| |
| try { |
| //Default Model |
| dataset.begin(ReadWrite.WRITE); |
| Model defaultModel = dataset.getDefaultModel(); |
| applyInferencing(geosparqlSchema, defaultModel, "default"); |
| |
| //Named Models |
| Iterator<String> graphNames = dataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| applyInferencing(geosparqlSchema, namedModel, graphName); |
| } |
| |
| dataset.commit(); |
| LOGGER.info("Applying GeoSPARQL Schema - Completed"); |
| } catch (Exception ex) { |
| LOGGER.error("Inferencing Error: {}", ex.getMessage()); |
| } finally { |
| dataset.end(); |
| } |
| } |
| |
| /** |
| * Apply GeoSPARQL inferencing using GeoSPARPQL v1.0 (corrected version) and |
| * RDFS reasoner.<br> |
| * Statements will be added to the dataModel. |
| * |
| * @param dataModel |
| */ |
| public static final void applyInferencing(Model dataModel) { |
| Model geosparqlSchema = loadGeoSPARQLSchema(); |
| applyInferencing(geosparqlSchema, dataModel); |
| } |
| |
| /** |
| * Apply GeoSPARQL inferencing using schema model and RDFS reasoner.<br> |
| * Statements will be added to the Model. |
| * |
| * @param geosparqlSchema |
| * @param model |
| */ |
| public static final void applyInferencing(Model geosparqlSchema, Model model) { |
| applyInferencing(geosparqlSchema, model, "unnamed"); |
| } |
| |
| /** |
| * Apply GeoSPARQL inferencing using schema model and RDFS reasoner.<br> |
| * Statements will be added to the Model.<br> |
| * Graph name supplied for logging purposes only. |
| * |
| * @param geosparqlSchema |
| * @param model |
| * @param graphName |
| */ |
| public static final void applyInferencing(Model geosparqlSchema, Model model, String graphName) { |
| if (!model.isEmpty()) { |
| InfModel infModel = ModelFactory.createRDFSModel(geosparqlSchema, model); |
| model.add(infModel); |
| LOGGER.info("GeoSPARQL schema applied to graph: {}", graphName); |
| } else { |
| LOGGER.info("GeoSPARQL schema not applied to empty graph: {}", graphName); |
| } |
| } |
| |
| /** |
| * Prepare an empty GeoSPARQL model with RDFS reasoning. |
| * <br> In-memory indexing applied by default. |
| * <br> This can be changed by calling GeoSPARQLConfig methods. |
| * |
| * @return Output model. |
| */ |
| public static final InfModel prepare() { |
| return prepareRDFS(ModelFactory.createDefaultModel()); |
| } |
| |
| /** |
| * Prepare a GeoSPARQL model from an existing model with RDFS reasoning. |
| * <br> In-memory indexing applied by default. |
| * <br> This can be changed by calling GeoSPARQLConfig methods. |
| * |
| * @param model |
| * @return Output model. |
| */ |
| public static final InfModel prepareRDFS(Model model) { |
| return prepare(model, ReasonerRegistry.getRDFSReasoner()); |
| } |
| |
| /** |
| * Prepare a GeoSPARQL model from an existing model with alternative |
| * Reasoner, e.g. OWL. |
| * <br> In-memory indexing applied by default. |
| * <br> This can be changed by calling GeoSPARQLConfig methods. |
| * |
| * @param model |
| * @param reasoner |
| * @return Output model. |
| */ |
| public static final InfModel prepare(Model model, Reasoner reasoner) { |
| InputStream geosparqlSchemaInputStream = GeoSPARQLOperations.class.getClassLoader().getResourceAsStream(GEOSPARQL_SCHEMA_FILE); |
| return prepare(geosparqlSchemaInputStream, model, reasoner); |
| } |
| |
| /** |
| * Prepare a GeoSPARQL model from file with RDFS reasoning. |
| * <br> In-memory indexing applied by default. |
| * <br> This can be changed by calling GeoSPARQLConfig methods. |
| * |
| * @param inputStream |
| * @return Output model. |
| */ |
| public static final InfModel prepareRDFS(InputStream inputStream) { |
| return prepare(inputStream, ReasonerRegistry.getRDFSReasoner()); |
| } |
| |
| /** |
| * Prepare a GeoSPARQL model from file with alternative Reasoner, e.g. OWL. |
| * <br> In-memory indexing applied by default. |
| * <br> This can be changed by calling GeoSPARQLConfig methods. |
| * |
| * @param inputStream |
| * @param reasoner |
| * @return Output model. |
| */ |
| public static final InfModel prepare(InputStream inputStream, Reasoner reasoner) { |
| Model model = ModelFactory.createDefaultModel(); |
| model.read(inputStream, null); |
| |
| return prepare(model, reasoner); |
| } |
| |
| /** |
| * Prepare a model from an existing model with alternative GeoSPARQL schema |
| * and Reasoner, e.g. OWL. |
| * <br> In-memory indexing applied by default. |
| * <br> This can be changed by calling GeoSPARQLConfig methods. |
| * |
| * @param geosparqlSchemaInputStream |
| * @param model |
| * @param reasoner |
| * @return Output model. |
| */ |
| public static final InfModel prepare(InputStream geosparqlSchemaInputStream, Model model, Reasoner reasoner) { |
| |
| //Load GeoSPARQL Schema |
| Model schema = ModelFactory.createDefaultModel(); |
| schema.read(geosparqlSchemaInputStream, null); |
| |
| //Apply the schema to the reasoner. |
| reasoner = reasoner.bindSchema(schema); |
| |
| //Setup inference model. |
| InfModel infModel = ModelFactory.createInfModel(reasoner, model); |
| |
| return infModel; |
| } |
| |
| /** |
| * Validate Geometry Literal in Dataset. |
| * |
| * @param dataset |
| * @return Whether dataset is valid. |
| */ |
| public static final boolean validateGeometryLiteral(Dataset dataset) { |
| |
| boolean isValid = true; |
| |
| LOGGER.info("Validate Geometry Literal - Started"); |
| //Default Model |
| dataset.begin(ReadWrite.READ); |
| Model defaultModel = dataset.getDefaultModel(); |
| GeoSPARQLOperations.validateGeometryLiteral(defaultModel); |
| |
| //Named Models |
| Iterator<String> graphNames = dataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| boolean isModelValid = GeoSPARQLOperations.validateGeometryLiteral(namedModel); |
| |
| if (!isModelValid) { |
| isValid = false; |
| } |
| } |
| |
| LOGGER.info("Validate Geometry Literal - Completed"); |
| dataset.end(); |
| |
| return isValid; |
| } |
| |
| /** |
| * Validate Geometry Literal in Model. |
| * |
| * @param model |
| * @return Whether model is valid. |
| */ |
| public static final boolean validateGeometryLiteral(Model model) { |
| |
| //Get current state of index and switch it off temporarily. |
| boolean isIndexActive = GeometryLiteralIndex.isIndexActive(); |
| GeometryLiteralIndex.setIndexActive(false); |
| |
| boolean isValid = true; |
| NodeIterator nodeIt = model.listObjectsOfProperty(Geo.HAS_DEFAULT_GEOMETRY_PROP); |
| while (nodeIt.hasNext()) { |
| RDFNode node = nodeIt.nextNode(); |
| |
| try { |
| Literal geometryLiteral = node.asLiteral(); |
| GeometryWrapper.extract(geometryLiteral); |
| } catch (DatatypeFormatException ex) { |
| //Error messages should already have been issued. Catch exception so can continue on whole dataset. |
| isValid = false; |
| } |
| } |
| |
| //Switch index back on if it was on. |
| GeometryLiteralIndex.setIndexActive(isIndexActive); |
| |
| return isValid; |
| } |
| |
| /** |
| * Find the most frequent SRS URI of Geometry Literals in the dataset. |
| * |
| * @param dataset |
| * @return SRS URI |
| */ |
| public static final String findModeSRS(Dataset dataset) throws SrsException { |
| LOGGER.info("Find Mode SRS - Started"); |
| ModeSRS modeSRS = new ModeSRS(); |
| //Default Model |
| dataset.begin(ReadWrite.READ); |
| Model defaultModel = dataset.getDefaultModel(); |
| modeSRS.search(defaultModel); |
| |
| //Named Models |
| Iterator<String> graphNames = dataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| modeSRS.search(namedModel); |
| } |
| |
| LOGGER.info("Find Mode SRS - Completed"); |
| dataset.end(); |
| |
| return modeSRS.getModeURI(); |
| } |
| |
| /** |
| * Find the most frequent SRS URI of Geometry Literals in the model. |
| * |
| * @param model |
| * @return SRS URI |
| */ |
| public static final String findModeSRS(Model model) throws SrsException { |
| ModeSRS modeSRS = new ModeSRS(); |
| modeSRS.search(model); |
| return modeSRS.getModeURI(); |
| } |
| |
| /** |
| * Converts all geometry literals (WKT or GML) from current SRS to the |
| * specified SRS. |
| * |
| * @param inputFile |
| * @param inputLang |
| * @param outputFile |
| * @param outputLang |
| * @param outputSrsURI |
| */ |
| public static final void convertFile(File inputFile, Lang inputLang, File outputFile, Lang outputLang, String outputSrsURI) { |
| convertFileSRSDatatype(inputFile, inputLang, outputFile, outputLang, outputSrsURI, null); |
| } |
| |
| /** |
| * Converts file between serialisations. |
| * |
| * @param inputFile |
| * @param inputLang |
| * @param outputFile |
| * @param outputLang |
| */ |
| public static final void convertFile(File inputFile, Lang inputLang, File outputFile, Lang outputLang) { |
| convertFileSRSDatatype(inputFile, inputLang, outputFile, outputLang, null, null); |
| } |
| |
| /** |
| * Converts all geometry literals (WKT or GML) from current SRS to the |
| * specified SRS and datatype. |
| * |
| * @param inputFile |
| * @param inputLang |
| * @param outputFile |
| * @param outputLang |
| * @param outputSrsURI |
| * @param outputDatatype |
| */ |
| public static final void convertFile(File inputFile, Lang inputLang, File outputFile, Lang outputLang, String outputSrsURI, GeometryDatatype outputDatatype) { |
| convertFileSRSDatatype(inputFile, inputLang, outputFile, outputLang, outputSrsURI, outputDatatype); |
| } |
| |
| /** |
| * Converts all geometry literals (WKT or GML) to the specified datatype. |
| * |
| * @param inputFile |
| * @param inputLang |
| * @param outputFile |
| * @param outputLang |
| * @param outputDatatype |
| */ |
| public static final void convertFile(File inputFile, Lang inputLang, File outputFile, Lang outputLang, GeometryDatatype outputDatatype) { |
| convertFileSRSDatatype(inputFile, inputLang, outputFile, outputLang, null, outputDatatype); |
| } |
| |
| private static Model convertSRSDatatype(Model inputModel, String outputSrsURI, GeometryDatatype outputDatatype) { |
| if (outputSrsURI == null) { |
| outputSrsURI = GeoSPARQLOperations.findModeSRS(inputModel); |
| } |
| if (outputDatatype == null || !GeometryDatatype.check(outputDatatype)) { |
| LOGGER.warn("Output datatype {} is not a recognised for Geometry Literal. Defaulting to {}.", outputDatatype, WKTDatatype.URI); |
| outputDatatype = WKTDatatype.INSTANCE; |
| } |
| |
| //Iterate through all statements: toNodeValue geometry literals and just add the rest. |
| Model outputModel = ModelFactory.createDefaultModel(); |
| outputModel.setNsPrefixes(inputModel.getNsPrefixMap()); |
| Iterator<Statement> statementIt = inputModel.listStatements(); |
| while (statementIt.hasNext()) { |
| Statement statement = statementIt.next(); |
| RDFNode object = statement.getObject(); |
| if (object.isLiteral()) { |
| handleLiteral(statement, outputModel, outputSrsURI, outputDatatype); |
| } else { |
| //Not a statement of interest so store for output. |
| outputModel.add(statement); |
| } |
| } |
| return outputModel; |
| } |
| |
| /** |
| * Convert a list of strings representation of geometry literals to another |
| * coordinate reference system. |
| * |
| * @param geometryLiterals |
| * @param outputSrsURI Coordinate reference system URI |
| * @param outputDatatype |
| * @return Output of conversion. |
| */ |
| public static final List<String> convertGeometryLiterals(List<String> geometryLiterals, String outputSrsURI, GeometryDatatype outputDatatype) { |
| List<String> outputGeometryLiterals = new ArrayList<>(geometryLiterals.size()); |
| for (String geometryLiteral : geometryLiterals) { |
| String convertedGeometryLiteral = convertGeometryLiteral(geometryLiteral, outputSrsURI, outputDatatype); |
| outputGeometryLiterals.add(convertedGeometryLiteral); |
| } |
| return outputGeometryLiterals; |
| } |
| |
| public static void writeOutputModel(Model outputModel, File outputFile, Lang outputLang, File inputFile) { |
| if (!outputModel.isEmpty()) { |
| try (final FileOutputStream outputStream = new FileOutputStream(outputFile)) { |
| RDFDataMgr.write(outputStream, outputModel, outputLang); |
| } catch (IOException ex) { |
| LOGGER.error("Output File IO Exception: {} - {}", outputFile.getAbsolutePath(), ex.getMessage()); |
| } |
| } else { |
| LOGGER.warn("Output Model is empty for {}: Did not create: {}", inputFile.getAbsolutePath(), outputFile.getAbsolutePath()); |
| } |
| } |
| |
| private static void handleLiteral(Statement statement, Model outputModel, String outputSrsURI, GeometryDatatype outputDatatype) { |
| Literal literal = statement.getLiteral(); |
| RDFDatatype datatype = literal.getDatatype(); |
| //Check whether a supported geometry literal. |
| if (GeometryDatatype.check(datatype)) { |
| GeometryWrapper originalGeom = GeometryWrapper.extract(literal); |
| GeometryWrapper convertedGeom; |
| try { |
| if (outputSrsURI != null) { |
| convertedGeom = originalGeom.convertSRS(outputSrsURI); |
| } else { |
| convertedGeom = originalGeom; |
| } |
| } catch (FactoryException | MismatchedDimensionException | TransformException ex) { |
| LOGGER.error("SRS Conversion Exception: {} - Literal: {}, Output SRS URI: {}. Reusing original literal for output.", ex.getMessage(), literal, outputSrsURI); |
| convertedGeom = originalGeom; |
| } |
| if (outputDatatype == null) { |
| outputDatatype = GeometryDatatype.get(datatype); |
| } |
| Literal convertedGeometryLiteral = convertedGeom.asLiteral(outputDatatype); |
| |
| // Assign the existing property unless it needs to be switched for asGML and asWKT. |
| Property outputProperty = statement.getPredicate(); |
| if (outputProperty.equals(Geo.AS_GML_PROP) && outputDatatype.equals(WKTDatatype.INSTANCE)) { |
| outputProperty = Geo.AS_WKT_PROP; |
| } else if (outputProperty.equals(Geo.AS_WKT_PROP) && outputDatatype.equals(GMLDatatype.INSTANCE)) { |
| outputProperty = Geo.AS_GML_PROP; |
| } |
| |
| Statement outputStatement = ResourceFactory.createStatement(statement.getSubject(), outputProperty, convertedGeometryLiteral); |
| outputModel.add(outputStatement); |
| } else { |
| //Not a statement of interest so store for output. |
| outputModel.add(statement); |
| } |
| } |
| |
| /** |
| * Only RDF files should be in the input folder and must all be the same RDF |
| * language. |
| * |
| * @param inputFolder |
| * @param inputLang |
| * @param outputFolder |
| * @param outputLang |
| * @param outputSrsURI |
| */ |
| public static final void convertFolder(File inputFolder, Lang inputLang, File outputFolder, Lang outputLang, String outputSrsURI) { |
| convertFolderSRSDatatype(inputFolder, inputLang, outputFolder, outputLang, outputSrsURI, null); |
| } |
| |
| /** |
| * Only RDF files should be in the input folder and must all be the same RDF |
| * language. |
| * |
| * @param inputFolder |
| * @param inputLang |
| * @param outputFolder |
| * @param outputLang |
| */ |
| public static final void convertFolder(File inputFolder, Lang inputLang, File outputFolder, Lang outputLang) { |
| convertFolderSRSDatatype(inputFolder, inputLang, outputFolder, outputLang, null, null); |
| } |
| |
| /** |
| * Only RDF files should be in the input folder and must all be the same RDF |
| * language. Output will be in the specified SRS and datatype/serialisation. |
| * |
| * @param inputFolder |
| * @param inputLang |
| * @param outputFolder |
| * @param outputLang |
| * @param outputSrsURI |
| * @param outputDatatype |
| */ |
| public static final void convertFolder(File inputFolder, Lang inputLang, File outputFolder, Lang outputLang, String outputSrsURI, GeometryDatatype outputDatatype) { |
| convertFolderSRSDatatype(inputFolder, inputLang, outputFolder, outputLang, outputSrsURI, outputDatatype); |
| } |
| |
| /** |
| * Only RDF files should be in the input folder and must all be the same RDF |
| * language. Output will be in the specified datatype/serialisation. |
| * |
| * @param inputFolder |
| * @param inputLang |
| * @param outputFolder |
| * @param outputLang |
| * @param outputDatatype |
| */ |
| public static final void convertFolder(File inputFolder, Lang inputLang, File outputFolder, Lang outputLang, GeometryDatatype outputDatatype) { |
| convertFolderSRSDatatype(inputFolder, inputLang, outputFolder, outputLang, null, outputDatatype); |
| } |
| |
| /** |
| * Convert Geo Predicates (Lat/Lon) in Dataset to WKT Geometry Literal.<br> |
| * Option to remove Lat and Lon predicates after combining. |
| * |
| * @param dataset |
| * @param isRemoveGeoPredicate |
| * @return Converted dataset. |
| * |
| */ |
| public static final Dataset convertGeoPredicates(Dataset dataset, boolean isRemoveGeoPredicate) { |
| LOGGER.info("Convert Geo Predicates - Started"); |
| Dataset outputDataset = DatasetFactory.createTxnMem(); |
| outputDataset.begin(ReadWrite.WRITE); |
| //Default Model |
| dataset.begin(ReadWrite.READ); |
| Model defaultModel = dataset.getDefaultModel(); |
| Model convertedModel = convertGeoPredicates(defaultModel, isRemoveGeoPredicate); |
| convertedModel.setNsPrefixes(defaultModel.getNsPrefixMap()); |
| outputDataset.setDefaultModel(convertedModel); |
| //Named Models |
| Iterator<String> graphNames = dataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| Model convertedNamedModel = convertGeoPredicates(namedModel, isRemoveGeoPredicate); |
| convertedNamedModel.setNsPrefixes(namedModel.getNsPrefixMap()); |
| outputDataset.addNamedModel(graphName, convertedNamedModel); |
| } |
| LOGGER.info("Convert Geo Predicates - Completed"); |
| dataset.end(); |
| outputDataset.commit(); |
| outputDataset.end(); |
| return outputDataset; |
| } |
| |
| /** |
| * Convert Geo Predicates (Lat/Lon) in Model to WKT Geometry Literal.<br> |
| * Option to remove Lat and Lon predicates after combining. |
| * |
| * @param model |
| * @param isRemoveGeoPredicates |
| * @return Converted model. |
| */ |
| public static final Model convertGeoPredicates(Model model, boolean isRemoveGeoPredicates) { |
| Model outputModel = ModelFactory.createDefaultModel(); |
| outputModel.add(model); |
| outputModel.setNsPrefixes(model.getNsPrefixMap()); |
| if (outputModel.containsResource(SpatialExtension.GEO_LAT_PROP)) { |
| ResIterator resIt = outputModel.listSubjectsWithProperty(SpatialExtension.GEO_LAT_PROP); |
| while (resIt.hasNext()) { |
| Resource feature = resIt.nextResource(); |
| if (feature.hasProperty(SpatialExtension.GEO_LON_PROP) && feature.hasProperty(SpatialExtension.GEO_LAT_PROP)) { |
| //Create a GeometryLiteral from Lat/Lon |
| Literal lat = feature.getProperty(SpatialExtension.GEO_LAT_PROP).getLiteral(); |
| Literal lon = feature.getProperty(SpatialExtension.GEO_LON_PROP).getLiteral(); |
| try { |
| Literal latLonPoint = ConvertLatLon.toLiteral(lat.getFloat(), lon.getFloat()); |
| Resource geometry = createGeometry(feature); |
| |
| //Add Geometry to Feature and GeometryLiteral to Geometry. |
| outputModel.add(feature, Geo.HAS_GEOMETRY_PROP, geometry); |
| outputModel.add(geometry, Geo.HAS_SERIALIZATION_PROP, latLonPoint); |
| outputModel.add(geometry, Geo.AS_WKT_PROP, latLonPoint); |
| } catch (DatatypeFormatException ex) { |
| LOGGER.error("Feature: {} has geo lat/lon out of bounds. Lat: {}, Lon: {}", feature, lat, lon); |
| } |
| } |
| } |
| if (isRemoveGeoPredicates) { |
| outputModel.removeAll(null, SpatialExtension.GEO_LAT_PROP, null); |
| outputModel.removeAll(null, SpatialExtension.GEO_LON_PROP, null); |
| } |
| } |
| return outputModel; |
| } |
| |
| private static Resource createGeometry(Resource feature) { |
| //Create a Geometry - re-use Feature if a URI or build a URI for blank node. |
| String geometryURI; |
| if (feature.isURIResource()) { |
| geometryURI = feature.getURI() + "-Geom-" + UUID.randomUUID().toString(); |
| } else { |
| geometryURI = GeoSPARQL_URI.GEO_URI + "Geom-" + UUID.randomUUID().toString(); |
| } |
| Resource geometry = ResourceFactory.createResource(geometryURI); |
| |
| return geometry; |
| } |
| |
| /** |
| * Convert Geometry Datatypes (WKT, GML, etc.) in Model to GeoSPARQL |
| * structure.<br> |
| * (Subject-property-GeometryLiteral) becomes (Feature-hasGeometry-Geometry) |
| * and (Geometry-hasSerialization-GeometryLiteral).<br> |
| * Original property will be removed from resulting Dataset. |
| * |
| * |
| * @param dataset |
| * @return Converted dataset. |
| * |
| */ |
| public static final Dataset convertGeometryStructure(Dataset dataset) { |
| LOGGER.info("Convert Geometry Structure - Started"); |
| Dataset outputDataset = DatasetFactory.createTxnMem(); |
| outputDataset.begin(ReadWrite.WRITE); |
| //Default Model |
| dataset.begin(ReadWrite.READ); |
| Model defaultModel = dataset.getDefaultModel(); |
| Model convertedModel = convertGeometryStructure(defaultModel); |
| convertedModel.setNsPrefixes(defaultModel.getNsPrefixMap()); |
| outputDataset.setDefaultModel(convertedModel); |
| //Named Models |
| Iterator<String> graphNames = dataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| Model convertedNamedModel = convertGeometryStructure(namedModel); |
| convertedNamedModel.setNsPrefixes(namedModel.getNsPrefixMap()); |
| outputDataset.addNamedModel(graphName, convertedNamedModel); |
| } |
| LOGGER.info("Convert Geometry Structure - Completed"); |
| dataset.end(); |
| outputDataset.commit(); |
| outputDataset.end(); |
| return outputDataset; |
| } |
| |
| /** |
| * Convert Geometry Datatypes (WKT, GML, etc.) in Model to GeoSPARQL |
| * structure.<br> |
| * (Subject-property-GeometryLiteral) becomes (Feature-hasGeometry-Geometry) |
| * and (Geometry-hasSerialization-GeometryLiteral).<br> |
| * Original property will be removed from resulting Model. |
| * |
| * @param model |
| * @return Converted model. |
| */ |
| public static final Model convertGeometryStructure(Model model) { |
| |
| Model outputModel = ModelFactory.createDefaultModel(); |
| outputModel.add(model); |
| outputModel.setNsPrefixes(model.getNsPrefixMap()); |
| |
| List<Statement> additionalStatements = new ArrayList<>(); |
| |
| StmtIterator stmtIter = outputModel.listStatements(); |
| |
| while (stmtIter.hasNext()) { |
| Statement stmt = stmtIter.nextStatement(); |
| RDFNode object = stmt.getObject(); |
| if (object.isLiteral()) { |
| Literal literal = object.asLiteral(); |
| RDFDatatype datatype = literal.getDatatype(); |
| if (GeometryDatatype.check(datatype)) { |
| |
| Property property = stmt.getPredicate(); |
| if (property.equals(Geo.HAS_SERIALIZATION_PROP) || property.equals(Geo.AS_WKT_PROP) || property.equals(Geo.AS_GML_PROP)) { |
| //Model already contains the GeoSPARQL properties for this literal so skipping. |
| continue; |
| } |
| |
| if (outputModel.contains(property, RDFS.subPropertyOf, Geo.HAS_SERIALIZATION_PROP)) { |
| //The property is a sub property of hasSerialization so skipping. Only RDFS inferencing needs to be applied. |
| continue; |
| } |
| |
| Resource feature = stmt.getSubject(); |
| Resource geometry = createGeometry(feature); |
| |
| additionalStatements.add(ResourceFactory.createStatement(feature, Geo.HAS_GEOMETRY_PROP, geometry)); |
| additionalStatements.add(ResourceFactory.createStatement(geometry, Geo.HAS_SERIALIZATION_PROP, literal)); |
| stmtIter.remove(); |
| } |
| } |
| } |
| |
| outputModel.add(additionalStatements); |
| |
| return outputModel; |
| } |
| |
| private static void convertFileSRSDatatype(File inputFile, Lang inputLang, File outputFile, Lang outputLang, String outputSrsURI, GeometryDatatype outputDatatype) { |
| LOGGER.info("Converting File: {} to {} in srs URI: {} - Started", inputFile.getAbsolutePath(), outputFile.getAbsolutePath(), outputSrsURI); |
| Model inputModel = ModelFactory.createDefaultModel(); |
| try (final FileInputStream inputStream = new FileInputStream(inputFile)) { |
| RDFDataMgr.read(inputModel, inputStream, inputLang); |
| } catch (IOException ex) { |
| LOGGER.error("Input File IO Exception: {} - {}", inputFile.getAbsolutePath(), ex.getMessage()); |
| } |
| Model outputModel = convertSRSDatatype(inputModel, outputSrsURI, outputDatatype); |
| //Write the output. |
| writeOutputModel(outputModel, outputFile, outputLang, inputFile); |
| LOGGER.info("Converting File: {} to {} in srs URI: {} - Completed", inputFile.getAbsolutePath(), outputFile.getAbsolutePath(), outputSrsURI); |
| } |
| |
| private static void convertFolderSRSDatatype(File inputFolder, Lang inputLang, File outputFolder, Lang outputLang, String outputSrsURI, GeometryDatatype outputDatatype) { |
| LOGGER.info("Converting Folder {} to {} in srs URI: {} - Started", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath(), outputSrsURI); |
| if (inputFolder.exists()) { |
| File[] inputFiles = inputFolder.listFiles(); |
| if (inputFiles.length > 0) { |
| outputFolder.mkdir(); |
| for (File inputFile : inputFiles) { |
| File outputFile = new File(outputFolder, inputFile.getName()); |
| try { |
| convertFile(inputFile, inputLang, outputFile, outputLang, outputSrsURI, outputDatatype); |
| } catch (Exception ex) { |
| LOGGER.error("{} for input {}. The output file {} may not be created.", ex.getMessage(), inputFile.getAbsolutePath(), outputFile.getAbsolutePath()); |
| } |
| } |
| } else { |
| LOGGER.warn("{} is empty. {} is not created.", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath()); |
| } |
| } else { |
| LOGGER.warn("{} does not exist. {} is not created.", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath()); |
| } |
| LOGGER.info("Converting Folder {} to {} in srs URI: {} - Completed", inputFolder.getAbsolutePath(), outputFolder.getAbsolutePath(), outputSrsURI); |
| } |
| |
| /** |
| * Convert a string representation of a geometry literal to another |
| * coordinate reference system. |
| * |
| * @param geometryLiteral |
| * @param outputSrsURI Coordinate reference system URI |
| * @param outputDatatype |
| * @return Output of conversion. |
| */ |
| public static final String convertGeometryLiteral(String geometryLiteral, String outputSrsURI, GeometryDatatype outputDatatype) { |
| Literal lit = ResourceFactory.createTypedLiteral(geometryLiteral, outputDatatype); |
| GeometryWrapper geometryWrapper = GeometryWrapper.extract(lit); |
| try { |
| GeometryWrapper transformedGeometryWrapper = geometryWrapper.convertSRS(outputSrsURI); |
| Literal transformedLit = transformedGeometryWrapper.asLiteral(); |
| return transformedLit.getLexicalForm(); |
| } catch (FactoryException | MismatchedDimensionException | TransformException ex) { |
| LOGGER.error("{} : {} : {}", ex.getMessage(), geometryLiteral, outputSrsURI); |
| return null; |
| } |
| } |
| |
| /** |
| * Convert the input model to the most frequent coordinate reference system |
| * and default datatype. |
| * |
| * @param inputModel |
| * @return Output of conversion. |
| */ |
| public static final Model convert(Model inputModel) { |
| return convertSRSDatatype(inputModel, null, null); |
| } |
| |
| /** |
| * Convert the input model to the output coordinate reference system. |
| * |
| * @param inputModel |
| * @param outputSrsURI |
| * @return Output of conversion. |
| */ |
| public static final Model convert(Model inputModel, String outputSrsURI) { |
| return convertSRSDatatype(inputModel, outputSrsURI, null); |
| } |
| |
| /** |
| * Convert the input model to the output geometry literal datatype. |
| * |
| * @param inputModel |
| * @param outputDatatype |
| * @return Output of conversion. |
| */ |
| public static final Model convert(Model inputModel, GeometryDatatype outputDatatype) { |
| return convertSRSDatatype(inputModel, null, outputDatatype); |
| } |
| |
| /** |
| * Convert the input model to the output coordinate reference system and |
| * geometry literal datatype. |
| * |
| * @param inputModel |
| * @param outputSrsURI |
| * @param outputDatatype |
| * @return Output of conversion. |
| */ |
| public static final Model convert(Model inputModel, String outputSrsURI, GeometryDatatype outputDatatype) { |
| return convertSRSDatatype(inputModel, outputSrsURI, outputDatatype); |
| } |
| |
| /** |
| * Convert the input model to the output coordinate reference system and |
| * geometry literal datatype. |
| * |
| * @param inputModel |
| * @param outputSrsURI |
| * @param outputDatatypeURI |
| * @return Output of conversion. |
| */ |
| public static final Model convert(Model inputModel, String outputSrsURI, String outputDatatypeURI) { |
| return convertSRSDatatype(inputModel, outputSrsURI, GeometryDatatype.get(outputDatatypeURI)); |
| } |
| |
| /** |
| * Convert the input dataset to the most frequent coordinate reference |
| * system and default datatype. |
| * |
| * @param dataset |
| * @return Converted dataset. |
| */ |
| public static final Dataset convert(Dataset dataset) { |
| return convert(dataset, null, null); |
| } |
| |
| /** |
| * Convert the input dataset to the output coordinate reference system. |
| * |
| * @param dataset |
| * @param outputSrsURI |
| * @return Converted dataset. |
| */ |
| public static final Dataset convert(Dataset dataset, String outputSrsURI) { |
| return convert(dataset, outputSrsURI, null); |
| } |
| |
| /** |
| * Convert the input dataset to the output geometry literal datatype. |
| * |
| * @param dataset |
| * @param outputDatatype |
| * @return Converted dataset. |
| */ |
| public static final Dataset convert(Dataset dataset, GeometryDatatype outputDatatype) { |
| return convert(dataset, null, outputDatatype); |
| } |
| |
| /** |
| * Convert the input dataset to the output coordinate reference system and |
| * geometry literal datatype. |
| * |
| * @param inputDataset |
| * @param outputSrsURI |
| * @param outputDatatype |
| * @return Converted dataset. |
| */ |
| public static final Dataset convert(Dataset inputDataset, String outputSrsURI, GeometryDatatype outputDatatype) { |
| LOGGER.info("Convert Dataset - Started SRS: {}, Datatype: {}", outputSrsURI, outputDatatype); |
| if (outputSrsURI == null) { |
| outputSrsURI = GeoSPARQLOperations.findModeSRS(inputDataset); |
| LOGGER.info("SRS URI not specified. Defaulting to most frequent SRS URI: {}", outputSrsURI); |
| } |
| if (outputDatatype == null || !GeometryDatatype.check(outputDatatype)) { |
| LOGGER.warn("Output datatype {} is not a recognised for Geometry Literal. Defaulting to {}.", outputDatatype, WKTDatatype.URI); |
| outputDatatype = WKTDatatype.INSTANCE; |
| } |
| Dataset dataset = DatasetFactory.createTxnMem(); |
| dataset.begin(ReadWrite.WRITE); |
| //Default Model |
| inputDataset.begin(ReadWrite.READ); |
| Model defaultModel = inputDataset.getDefaultModel(); |
| Model convertedModel = convertSRSDatatype(defaultModel, outputSrsURI, outputDatatype); |
| dataset.setDefaultModel(convertedModel); |
| //Named Models |
| Iterator<String> graphNames = inputDataset.listNames(); |
| while (graphNames.hasNext()) { |
| String graphName = graphNames.next(); |
| Model namedModel = inputDataset.getNamedModel(graphName); |
| Model convertedNamedModel = convertSRSDatatype(namedModel, outputSrsURI, outputDatatype); |
| dataset.addNamedModel(graphName, convertedNamedModel); |
| } |
| LOGGER.info("Convert Dataset - Completed SRS: {}, Datatype: {}", outputSrsURI, outputDatatype); |
| dataset.commit(); |
| dataset.end(); |
| inputDataset.end(); |
| return dataset; |
| } |
| |
| /** |
| * |
| * @param model |
| * @param graphName Name of graph for logging purposes. |
| * @return Number of Geometry Literals contained in Model. |
| */ |
| public static final int countGeometryLiterals(Model model, String graphName) { |
| Set<String> literalStrings = new TreeSet<>(); |
| ExtendedIterator<Statement> hasSerializationIterator = model.listStatements(null, Geo.HAS_SERIALIZATION_PROP, (RDFNode) null); |
| ExtendedIterator<Statement> asWKTIterator = model.listStatements(null, Geo.AS_WKT_PROP, (RDFNode) null); |
| ExtendedIterator<Statement> asGMLIterator = model.listStatements(null, Geo.AS_GML_PROP, (RDFNode) null); |
| |
| ExtendedIterator<Statement> allIterator = hasSerializationIterator.andThen(asWKTIterator).andThen(asGMLIterator); |
| int count = 0; |
| while (allIterator.hasNext()) { |
| Statement st = allIterator.next(); |
| String literalString = st.getLiteral().getString(); |
| literalStrings.add(literalString); |
| count++; |
| } |
| LOGGER.info("Graph: {} has {} unique out of {} Geometry Literals.", graphName, literalStrings.size(), count); |
| return count; |
| } |
| |
| /** |
| * |
| * @param dataset |
| * @return Count of Geometry Literals in whole Dataset. |
| */ |
| public static final int countGeometryLiterals(Dataset dataset) { |
| dataset.begin(ReadWrite.READ); |
| Model defaultModel = dataset.getDefaultModel(); |
| int count = countGeometryLiterals(defaultModel, "Default Model"); |
| Iterator<String> iterator = dataset.listNames(); |
| while (iterator.hasNext()) { |
| String graphName = iterator.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| count += countGeometryLiterals(namedModel, graphName); |
| } |
| dataset.end(); |
| return count; |
| } |
| |
| /** |
| * Apply a set of commonly used prefixes for GeoSPARQL URIs to the Model. |
| * |
| * @param model |
| */ |
| public static final void applyPrefixes(Model model) { |
| HashMap<String, String> geoPrefixes = GeoSPARQL_URI.getPrefixes(); |
| model.setNsPrefixes(geoPrefixes); |
| } |
| |
| /** |
| * Apply a set of commonly used prefixes for GeoSPARQL URIs to the whole |
| * Dataset. |
| * |
| * @param dataset |
| */ |
| public static final void applyPrefixes(Dataset dataset) { |
| dataset.begin(ReadWrite.READ); |
| Model defaultModel = dataset.getDefaultModel(); |
| applyPrefixes(defaultModel); |
| Iterator<String> iterator = dataset.listNames(); |
| while (iterator.hasNext()) { |
| String graphName = iterator.next(); |
| Model namedModel = dataset.getNamedModel(graphName); |
| applyPrefixes(namedModel); |
| } |
| dataset.commit(); |
| dataset.end(); |
| } |
| |
| } |