/* * Geopaparazzi - Digital field mapping on Android based devices * Copyright (C) 2016 HydroloGIS (www.hydrologis.com) * * This program 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 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. * * 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.geopaparazzi.core.maptools; import android.content.Context; import android.content.Intent; import android.graphics.Canvas; import android.graphics.Paint; import android.net.Uri; import com.vividsolutions.jts.android.ShapeWriter; import com.vividsolutions.jts.android.geom.DrawableShape; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.GeometryFactory; import com.vividsolutions.jts.geom.Polygon; import com.vividsolutions.jts.geom.PrecisionModel; import com.vividsolutions.jts.geom.util.LinearComponentExtracter; import com.vividsolutions.jts.io.WKBReader; import com.vividsolutions.jts.io.WKBWriter; import com.vividsolutions.jts.noding.snapround.GeometryNoder; import com.vividsolutions.jts.operation.polygonize.Polygonizer; import java.util.ArrayList; import java.util.Collection; import java.util.List; import eu.geopaparazzi.library.database.GPLog; import eu.geopaparazzi.library.features.Feature; import eu.geopaparazzi.library.util.types.EDataType; import eu.geopaparazzi.spatialite.database.spatial.SpatialiteSourcesManager; import eu.geopaparazzi.spatialite.database.spatial.core.daos.DaoSpatialite; import eu.geopaparazzi.spatialite.database.spatial.core.databasehandlers.SpatialiteDatabaseHandler; import eu.geopaparazzi.spatialite.database.spatial.core.enums.GeometryType; import eu.geopaparazzi.spatialite.database.spatial.core.tables.SpatialVectorTable; import jsqlite.Database; import jsqlite.Exception; import jsqlite.Stmt; /** * A spatial feature container. * * @author Andrea Antonello (www.hydrologis.com) */ @SuppressWarnings("nls") public class FeatureUtilities { /** * Key to pass featuresLists through activities. */ public static final String KEY_FEATURESLIST = "KEY_FEATURESLIST"; /** * Key to pass a readonly flag through activities. */ public static final String KEY_READONLY = "KEY_READONLY"; /** * Key to pass a geometry type through activities. */ public static final String KEY_GEOMETRYTYPE = "KEY_GEOMETRYTYPE"; /** * A well known binary reader to use for geometry deserialization. */ public static WKBReader WKBREADER = new WKBReader(); /** * A well known binary writer to use for geometry serialization. */ public static WKBWriter WKBWRITER = new WKBWriter(); /** * Build the features given by a query. * <p/> * <b>Note that it is mandatory that the first item of the * query is the id of the feature, which can be used at any time * to update the feature in the db. * * @param query the query to run. * @param spatialTable the parent Spatialtable. * @return the list of feature from the query. * @throws Exception is something goes wrong. */ public static List<Feature> buildWithoutGeometry(String query, SpatialVectorTable spatialTable) throws Exception { List<Feature> featuresList = new ArrayList<>(); Stmt stmt = null; try { SpatialiteDatabaseHandler spatialiteDbHandler = SpatialiteSourcesManager.INSTANCE.getExistingDatabaseHandlerByTable(spatialTable); if (spatialiteDbHandler == null) { GPLog.addLogEntry("Featureutilities", "ERROR, could not get spatialiteDbHandler for spatialTable: " + spatialTable.toString()); return featuresList; } Database database = spatialiteDbHandler.getDatabase(); String tableName = spatialTable.getTableName(); String databasePath = spatialTable.getDatabasePath(); stmt = database.prepare(query); while (stmt.step()) { int column_count = stmt.column_count(); // the first is the id, transparent to the user String id = stmt.column_string(0); Feature feature = new Feature(tableName, databasePath, id); for (int i = 1; i < column_count; i++) { String cName = stmt.column_name(i); String value = stmt.column_string(i); EDataType type = spatialTable.getTableFieldType(cName); feature.addAttribute(cName, value, type.name()); } featuresList.add(feature); } } finally { if (stmt != null) stmt.close(); } return featuresList; } /** * Build the features given by a query. * <p/> * <p><b>Note that this query needs to have at least 2 arguments, the first * being the ROWID and the last the geometry. Else if will fail.</b> * * @param query the query to run. * @param spatialTable the parent Spatialtable. * @return the list of feature from the query. * @throws Exception is something goes wrong. */ public static List<Feature> buildFeatures(String query, SpatialVectorTable spatialTable) throws Exception { List<Feature> featuresList = new ArrayList<>(); SpatialiteDatabaseHandler spatialiteDbHandler = SpatialiteSourcesManager.INSTANCE.getExistingDatabaseHandlerByTable(spatialTable); Database database = spatialiteDbHandler.getDatabase(); String tableName = spatialTable.getTableName(); String databasePath = spatialTable.getDatabasePath(); Stmt stmt = database.prepare(query); try { while (stmt.step()) { int count = stmt.column_count(); String id = stmt.column_string(0); byte[] geometryBytes = stmt.column_bytes(count - 1); Feature feature = new Feature(tableName, databasePath, id, geometryBytes); for (int i = 1; i < count - 1; i++) { String cName = stmt.column_name(i); String value = stmt.column_string(i); EDataType type = spatialTable.getTableFieldType(cName); if (type == null) { GPLog.addLogEntry("Featureutilities#buildFeatures", "Unexpected type for column " + cName); continue; } feature.addAttribute(cName, value, type.name()); } featuresList.add(feature); } } finally { stmt.close(); } for (Feature feature : featuresList) { String id = feature.getId(); double[] areaLength = DaoSpatialite.getAreaAndLengthById(id, spatialTable); feature.setOriginalArea(areaLength[0]); feature.setOriginalLength(areaLength[1]); } return featuresList; } // /** // * Build the features given by a query. // * // * <p><b>Note that this query needs to have at least 2 arguments, the first // * being the ROWID and the second the geometry. Else if will fail.</b> // * // * @param query the query to run. // * @param spatialTable the parent Spatialtable. // * @return the list of feature from the query. // * @throws Exception is something goes wrong. // */ // public static List<Feature> buildRowidGeometryFeatures( String query, SpatialVectorTable spatialTable ) throws Exception { // // List<Feature> featuresList = new ArrayList<Feature>(); // AbstractSpatialDatabaseHandler vectorHandler = SpatialDatabasesManager.getInstance().getVectorHandler(spatialTable); // if (vectorHandler instanceof SpatialiteDatabaseHandler) { // SpatialiteDatabaseHandler spatialiteDbHandler = (SpatialiteDatabaseHandler) vectorHandler; // Database database = spatialiteDbHandler.getDatabase(); // String tableName = spatialTable.getTableName(); // String uniqueNameBasedOnDbFilePath = spatialTable.getUniqueNameBasedOnDbFilePath(); // // Stmt stmt = database.prepare(query); // try { // while( stmt.step() ) { // String id = stmt.column_string(0); // byte[] geometryBytes = stmt.column_bytes(1); // Feature feature = new Feature(tableName, uniqueNameBasedOnDbFilePath, id, geometryBytes); // featuresList.add(feature); // } // } finally { // stmt.close(); // } // } // return featuresList; // } /** * Draw a geometry on a canvas. * * @param geom the {@link Geometry} to draw. * @param canvas the {@link Canvas}. * @param shapeWriter the shape writer. * @param geometryPaintFill the fill. * @param geometryPaintStroke the stroke. */ public static void drawGeometry(Geometry geom, Canvas canvas, ShapeWriter shapeWriter, Paint geometryPaintFill, Paint geometryPaintStroke) { String geometryTypeStr = geom.getGeometryType(); int geometryTypeInt = GeometryType.forValue(geometryTypeStr); GeometryType geometryType = GeometryType.forValue(geometryTypeInt); DrawableShape shape = shapeWriter.toShape(geom); switch (geometryType) { case POINT_XY: case POINT_XYM: case POINT_XYZ: case POINT_XYZM: case MULTIPOINT_XY: case MULTIPOINT_XYM: case MULTIPOINT_XYZ: case MULTIPOINT_XYZM: { if (geometryPaintFill != null) shape.fill(canvas, geometryPaintFill); if (geometryPaintStroke != null) shape.draw(canvas, geometryPaintStroke); } break; case LINESTRING_XY: case LINESTRING_XYM: case LINESTRING_XYZ: case LINESTRING_XYZM: case MULTILINESTRING_XY: case MULTILINESTRING_XYM: case MULTILINESTRING_XYZ: case MULTILINESTRING_XYZM: { if (geometryPaintStroke != null) shape.draw(canvas, geometryPaintStroke); } break; case POLYGON_XY: case POLYGON_XYM: case POLYGON_XYZ: case POLYGON_XYZM: case MULTIPOLYGON_XY: case MULTIPOLYGON_XYM: case MULTIPOLYGON_XYZ: case MULTIPOLYGON_XYZM: { if (geometryPaintFill != null) shape.fill(canvas, geometryPaintFill); if (geometryPaintStroke != null) shape.draw(canvas, geometryPaintStroke); } break; default: break; } } /** * Get geometry from feature. * * @param feature the feature. * @return the {@link Geometry} or <code>null</code>. * @throws java.lang.Exception if something goes wrong. */ public static Geometry getGeometry(Feature feature) throws java.lang.Exception { byte[] defaultGeometry = feature.getDefaultGeometry(); if (defaultGeometry == null) { return null; } return WKBREADER.read(defaultGeometry); } /** * Tries to split an invalid polygon in its {@link GeometryCollection}. * <p/> * <p>Based on JTSBuilder code. * * @param invalidPolygon the invalid polygon. * @return the geometries. */ @SuppressWarnings("rawtypes") public static Geometry invalidPolygonSplit(Geometry invalidPolygon) { PrecisionModel pm = new PrecisionModel(10000000); GeometryFactory geomFact = invalidPolygon.getFactory(); List lines = LinearComponentExtracter.getLines(invalidPolygon); List nodedLinework = new GeometryNoder(pm).node(lines); // union the noded linework to remove duplicates Geometry nodedDedupedLinework = geomFact.buildGeometry(nodedLinework).union(); // polygonize the result Polygonizer polygonizer = new Polygonizer(); polygonizer.add(nodedDedupedLinework); Collection polys = polygonizer.getPolygons(); // convert to collection for return Polygon[] polyArray = GeometryFactory.toPolygonArray(polys); return geomFact.createGeometryCollection(polyArray); } /** * Get the SpatialVectorTable from the Feature. * * @param feature teh feature to get the table from. * @return the table or <code>null</code>. * @throws Exception */ public static SpatialVectorTable getTableFromFeature(Feature feature) throws Exception { SpatialVectorTable table = SpatialiteSourcesManager.INSTANCE.getTableFromFeature(feature); return table; } /** * Checks if the text is a vievable string (ex urls or files) and if yes, opens the intent. * * @param context the context to use. * @param text the text to check. * @return <code>true</code> if the text is viewable. */ public static void viewIfApplicable(Context context, String text) { String textLC = text.toLowerCase(); Intent intent = null; if (textLC.startsWith("http")) { intent = new Intent(Intent.ACTION_VIEW, Uri.parse(text)); context.startActivity(intent); } else if (textLC.endsWith("png")) { intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + text), "image/png"); } else if (textLC.endsWith("jpg")) { intent = new Intent(); intent.setAction(Intent.ACTION_VIEW); intent.setDataAndType(Uri.parse("file://" + text), "image/jpg"); } if (intent != null) context.startActivity(intent); } }