/******************************************************************************* * Copyright (c) 2014 Open Door Logistics (www.opendoorlogistics.com) * All rights reserved. This program and the accompanying materials * are made available under the terms of the GNU Lesser Public License v3 * which accompanies this distribution, and is available at http://www.gnu.org/licenses/lgpl.txt ******************************************************************************/ package com.opendoorlogistics.core.geometry; import java.io.File; import java.io.IOException; import java.net.URL; import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.commons.io.FilenameUtils; import org.geotools.data.DataStore; import org.geotools.data.DataStoreFinder; import org.geotools.data.simple.SimpleFeatureCollection; import org.geotools.data.simple.SimpleFeatureIterator; import org.geotools.data.simple.SimpleFeatureSource; import org.geotools.factory.CommonFactoryFinder; import org.geotools.geometry.jts.JTS; import org.opengis.feature.simple.SimpleFeature; import org.opengis.feature.simple.SimpleFeatureType; import org.opengis.feature.type.AttributeType; import org.opengis.filter.FilterFactory2; import org.opengis.filter.Id; import org.opengis.filter.identity.FeatureId; import org.opengis.referencing.crs.CoordinateReferenceSystem; import org.opengis.referencing.operation.MathTransform; import com.opendoorlogistics.api.components.PredefinedTags; import com.opendoorlogistics.api.geometry.ODLGeom; import com.opendoorlogistics.api.tables.ODLColumnType; import com.opendoorlogistics.api.tables.ODLDatastoreAlterable; import com.opendoorlogistics.api.tables.ODLTableAlterable; import com.opendoorlogistics.core.AppConstants; import com.opendoorlogistics.core.geometry.rog.ODLRenderOptimisedGeom; import com.opendoorlogistics.core.geometry.rog.RogReaderUtils; import com.opendoorlogistics.core.geometry.rog.RogSingleton; import com.opendoorlogistics.core.tables.ColumnValueProcessor; import com.opendoorlogistics.core.tables.beans.BeanTypeConversion; import com.opendoorlogistics.core.tables.memory.ODLDatastoreImpl; import com.opendoorlogistics.core.tables.utils.TableUtils; import com.opendoorlogistics.core.utils.LargeList; import com.opendoorlogistics.core.utils.io.RelativeFiles; import com.opendoorlogistics.core.utils.strings.Strings; import com.vividsolutions.jts.geom.Geometry; import com.vividsolutions.jts.geom.GeometryCollection; import com.vividsolutions.jts.geom.Polygon; public final class ImportShapefile { // static SimpleFeatureCollection selectFeaturesById(SimpleFeatureSource source, Set<String> ids) { // FilterFactory2 factory = CommonFactoryFinder.getFilterFactory2(null); // // HashSet<FeatureId> set = new HashSet<FeatureId>(); // for (String id : ids) { // FeatureId fid = factory.featureId(id); // set.add(fid); // } // Id filter = factory.id(set); // try { // return source.getFeatures(filter); // } catch (Throwable e) { // throw new RuntimeException(e); // } // } /** * Find and return all polygon objects within the geometry. * @param geometry * @return */ public static List<Polygon> findPolygons(Geometry geometry){ final ArrayList<Polygon> ret = new ArrayList<>(); class Recursor{ void recurse(Geometry g){ if(g==null){ return; } if(Polygon.class.isInstance(g)){ ret.add((Polygon)g); } else if(GeometryCollection.class.isInstance(g)){ int n = g.getNumGeometries(); for(int i =0 ; i<n;i++){ recurse(g.getGeometryN(i)); } } } } new Recursor().recurse(geometry); return ret; } public static ODLDatastoreAlterable<ODLTableAlterable> importShapefile(File file, boolean isLinkedGeometry) { ODLDatastoreAlterable<ODLTableAlterable> ds = ODLDatastoreImpl.alterableFactory.create(); importShapefile(file,isLinkedGeometry, ds,false); return ds; } public static DataStore openDataStore(File file){ Map<String, URL> map = new HashMap<String, URL>(); try { map.put("url", file.toURL()); return DataStoreFinder.getDataStore(map); } catch (Throwable e) { throw new RuntimeException(e); } } /** * Import the shapefile. All geometry is transformed into WGS84. * * @param file * @param ds */ @SuppressWarnings("deprecation") public static HashMap<ShapefileLink, ODLGeom> importShapefile(File file,boolean isLinkedGeometry, ODLDatastoreAlterable<? extends ODLTableAlterable> ds, boolean returnGeometry) { Spatial.initSpatial(); file = RelativeFiles.validateRelativeFiles(file.getPath(), AppConstants.SHAPEFILES_DIRECTORY); if(file==null){ return new HashMap<>(); } // check if we're actually opening a render optimised geometry file... do something if we are? String ext = FilenameUtils.getExtension(file.getAbsolutePath()); boolean isRog = Strings.equalsStd(ext, RogReaderUtils.RENDER_GEOMETRY_FILE_EXT); File originalFile = file; List<ODLRenderOptimisedGeom> rogs = null; if(isRog){ rogs = new LargeList<>(); RogSingleton.singleton().createLoader(file,rogs); // get the shapefile from the main one String shpFile = FilenameUtils.removeExtension(file.getPath()) + ".shp"; file = new File(shpFile); } SimpleFeatureIterator it = null; DataStore shapefile = null; // create return object HashMap<ShapefileLink, ODLGeom> ret = null; if(returnGeometry){ ret = new HashMap<>(); } try { shapefile = openDataStore(file); if(shapefile==null){ throw new RuntimeException("Could not open shapefile: " + file); } // get the linkfile using the *original file*, not the potentially redirected file String linkFile = RelativeFiles.getFilenameToSaveInLink(originalFile, AppConstants.SHAPEFILES_DIRECTORY); for (String type : shapefile.getTypeNames()) { // make table ODLTableAlterable table =null; if(ds!=null){ String tableName = type; if (TableUtils.findTable(ds, tableName) != null) { tableName = TableUtils.getUniqueNumberedTableName(type, ds); } table= ds.createTable(type, -1); } // add columns for each usable feature SimpleFeatureType schema = shapefile.getSchema(type); int nAttrib = schema.getAttributeCount(); int[] mapped = new int[nAttrib]; Arrays.fill(mapped, -1); if(ds!=null){ for (int i = 0; i < nAttrib; i++) { AttributeType attributeType = schema.getType(i); Class<?> binding = attributeType.getBinding(); ODLColumnType colType = BeanTypeConversion.getInternalType(binding); if (colType != null) { String attributeName = schema.getDescriptor(i).getLocalName(); if (table.addColumn(i, attributeName, colType, 0)!=-1) { mapped[i] = table.getColumnCount() - 1; if (colType == ODLColumnType.GEOM) { table.setColumnTags(mapped[i], Strings.toTreeSet(PredefinedTags.GEOMETRY)); } } } } } // get coord transform to turn into wgs84 long-lat MathTransform toWGS84 = getTransformToWGS84(shapefile, type); SimpleFeatureSource source = shapefile.getFeatureSource(type); SimpleFeatureCollection collection = source.getFeatures(); // parse all features recording all attributes, including geometry it = collection.features(); int objectIndex=0; while (it.hasNext()) { SimpleFeature feature = it.next(); //System.out.println(feature.getID()); if (SimpleFeature.class.isInstance(feature)) { SimpleFeature sf = (SimpleFeature) feature; // create row if we're outputting to a datastore int row=-1; if(ds!=null){ row = table.createEmptyRow(-1); } for (int i = 0; i < nAttrib; i++) { Object value = sf.getAttribute(i); // process geometry ShapefileLink link =null; if(value!=null && Geometry.class.isInstance(value)){ if(!isLinkedGeometry || ret!=null){ if(rogs!=null){ value = rogs.get(objectIndex); }else{ // Transform the geometry to wgs84 if we need it value = JTS.transform((Geometry)value, toWGS84); value = new ODLLoadedGeometry((Geometry)value); } }else{ // Geometry not needed value = null; } // Create geometry link link = new ShapefileLink(linkFile, type, sf.getID()); // Save the transformed geometry if flagged if(ret!=null){ ret.put(link,(ODLGeom)value); } // If we're using linked geometry the value for the table is the link if(isLinkedGeometry){ value = new ODLShapefileLinkGeom(link); } } // save to table if mapped int col = mapped[i]; if (col != -1) { ODLColumnType odlType = table.getColumnType(col); value = ColumnValueProcessor.convertToMe(odlType,value); table.setValueAt(value, row, col); } } objectIndex++; } else { throw new RuntimeException(); } } } } catch (Throwable e) { throw new RuntimeException(e); } finally { if (it != null) { it.close(); } if (shapefile != null) { shapefile.dispose(); } } return ret; } public static MathTransform getTransformToWGS84(DataStore shapefile, String type) throws IOException { CoordinateReferenceSystem crs = shapefile.getSchema(type).getCoordinateReferenceSystem(); MathTransform toWGS84 = Spatial.toWgs84(crs); return toWGS84; } public static void main(String[] args) { for(String filename : new String[]{ "C:\\Processing\\all\\districts.shp", "C:\\Processing\\all\\districts2.shp", "districts2.shp", "dir2\\dir3\\districts2.shp" }){ ShapefileLink link = new ShapefileLink(filename, "", ""); try { File validated = RelativeFiles.validateRelativeFiles(link.getFile(), AppConstants.SHAPEFILES_DIRECTORY); String saveAs = RelativeFiles.getFilenameToSaveInLink(validated, AppConstants.SHAPEFILES_DIRECTORY); System.out.println("Load from: "+ validated + " Save as:" + saveAs); } catch (Throwable e) { e.printStackTrace(); } } // // C:\Users\Phil\Dropbox\Business\Data\Shapefiles\Antartica\natural.shp // String filename = "C:\\Processing\\all\\districts.shp"; // // System.out.println("Starting1 " + new Date()); // importShapefile(new File(filename),false); // System.out.println("Finished1 " + new Date()); // // System.out.println("Starting2 " + new Date()); // for(int i =0 ;i <2767 ; i++){ // ShapefileLink link = new ShapefileLink(filename, "districts", "districts." + i); // Object geometry = Spatial.loadLink(link); // //System.out.println(link + "->" + (geometry==null? "Not loaded" : "Loaded")); // } // System.out.println("Finishing2 " + new Date()); // // System.out.println("Starting3 " + new Date()); // importShapefile(new File(filename),false); // System.out.println("Finished3 " + new Date()); } }