2017-01-10 15 views
1

我正在尝试编写一个Java程序,将大量的GPS坐标捕捉到一个line shapefile(一个道路网络),并且不仅返回新的co从属,但是线段捕捉到的唯一标识符。这个标识符是否为FID,在其他语言中使用的“索引”(即1是第一个特征等)还是属性表中的任何列都无关紧要。Java地理工具:捕捉到线条识别线被捕获到

我已经使用maptools::snapPointsToLines功能R中做到了这一点,但这是不能扩展给定的数据我需要处理的体积,所以我在寻找Java来更快速地处理数据,分析R.

我的代码(下面)与geotools捕捉教程非常相似,我在GPS点(1900万行)CSV中读取的细微差异而不是生成它们,我写了一个CSV结果。它很好,比我得到的快得多,但我不知道如何识别对齐的线。可用的文档似乎涵盖了查询和功能集上的过滤功能,我不能特别适用于此代码创建的索引行对象,并且我的代码toString()中的现有功能返回了一些难以理解的功能,例如[email protected]

基本上,我只想要lineID字段来产生任何其他GIS软件或语言可以匹配到特定路段的内容。

package org.geotools.tutorial.quickstart; 

import java.io.*; 
import java.util.List; 
import java.util.Arrays; 

import com.vividsolutions.jts.geom.Coordinate; 
import com.vividsolutions.jts.geom.Envelope; 
import com.vividsolutions.jts.geom.Geometry; 
import com.vividsolutions.jts.geom.LineString; 
import com.vividsolutions.jts.geom.MultiLineString; 
import com.vividsolutions.jts.index.SpatialIndex; 
import com.vividsolutions.jts.index.strtree.STRtree; 
import com.vividsolutions.jts.linearref.LinearLocation; 
import com.vividsolutions.jts.linearref.LocationIndexedLine; 

import org.geotools.data.FeatureSource; 
import org.geotools.data.FileDataStore; 
import org.geotools.data.FileDataStoreFinder; 
import org.geotools.feature.FeatureCollection; 
import org.geotools.geometry.jts.ReferencedEnvelope; 
import org.geotools.swing.data.JFileDataStoreChooser; 
import org.geotools.util.NullProgressListener; 
import org.opengis.feature.Feature; 
import org.opengis.feature.FeatureVisitor; 
import org.opengis.feature.simple.SimpleFeature; 
import com.opencsv.*; 

public class SnapToLine { 

    public static void main(String[] args) throws Exception { 

     /* 
     * Open a shapefile. You should choose one with line features 
     * (LineString or MultiLineString geometry) 
     * 
     */ 
     File file = JFileDataStoreChooser.showOpenFile("shp", null); 
     if (file == null) { 
      return; 
     } 

     FileDataStore store = FileDataStoreFinder.getDataStore(file); 
     FeatureSource source = store.getFeatureSource(); 

     // Check that we have line features 
     Class<?> geomBinding = source.getSchema().getGeometryDescriptor().getType().getBinding(); 
     boolean isLine = geomBinding != null 
       && (LineString.class.isAssignableFrom(geomBinding) || 
        MultiLineString.class.isAssignableFrom(geomBinding)); 

     if (!isLine) { 
      System.out.println("This example needs a shapefile with line features"); 
      return; 
     } 
     final SpatialIndex index = new STRtree(); 
     FeatureCollection features = source.getFeatures(); 
     //FeatureCollection featurecollection = source.getFeatures(Query.FIDS); 
     System.out.println("Slurping in features ..."); 
     features.accepts(new FeatureVisitor() { 

      @Override 
      public void visit(Feature feature) { 
       SimpleFeature simpleFeature = (SimpleFeature) feature; 
       Geometry geom = (MultiLineString) simpleFeature.getDefaultGeometry(); 
       // Just in case: check for null or empty geometry 
       if (geom != null) { 
        Envelope env = geom.getEnvelopeInternal(); 
        if (!env.isNull()) { 
         index.insert(env, new LocationIndexedLine(geom)); 
        } 
       } 
      } 
     }, new NullProgressListener()); 
/* 

/* 
     * We defined the maximum distance that a line can be from a point 
     * to be a candidate for snapping 
     */ 

     ReferencedEnvelope bounds = features.getBounds(); 
     final double MAX_SEARCH_DISTANCE = bounds.getSpan(0)/1000.0; 



     int pointsProcessed = 0; 
     int pointsSnapped = 0; 
     long elapsedTime = 0; 
     long startTime = System.currentTimeMillis(); 
     double longiOut; 
     double latiOut; 
     int moved; 
     String lineID = "NA"; 

     //Open up the CSVReader. Reading in line by line to avoid memory failure. 

     CSVReader csvReader = new CSVReader(new FileReader(new File("fakedata.csv"))); 
     String[] rowIn; 



     //open up the CSVwriter 
     String outcsv = "fakedataOUT.csv"; 
     CSVWriter writer = new CSVWriter(new FileWriter(outcsv)); 



     while ((rowIn = csvReader.readNext()) != null) { 

      // Get point and create search envelope 
      pointsProcessed++; 
      double longi = Double.parseDouble(rowIn[0]); 
      double lati = Double.parseDouble(rowIn[1]); 
      Coordinate pt = new Coordinate(longi, lati); 
      Envelope search = new Envelope(pt); 
      search.expandBy(MAX_SEARCH_DISTANCE); 

      /* 
      * Query the spatial index for objects within the search envelope. 
      * Note that this just compares the point envelope to the line envelopes 
      * so it is possible that the point is actually more distant than 
      * MAX_SEARCH_DISTANCE from a line. 
      */ 
      List<LocationIndexedLine> lines = index.query(search); 

      // Initialize the minimum distance found to our maximum acceptable 
      // distance plus a little bit 
      double minDist = MAX_SEARCH_DISTANCE + 1.0e-6; 
      Coordinate minDistPoint = null; 

      for (LocationIndexedLine line : lines) { 
       LinearLocation here = line.project(pt); 
       Coordinate point = line.extractPoint(here); 
       double dist = point.distance(pt); 
       if (dist < minDist) { 
        minDist = dist; 
        minDistPoint = point; 
        lineID = line.toString(); 
       } 
      } 


      if (minDistPoint == null) { 
       // No line close enough to snap the point to 
       System.out.println(pt + "- X"); 
       longiOut = longi; 
       latiOut = lati; 
       moved = 0; 
       lineID = "NA"; 
      } else { 
       System.out.printf("%s - snapped by moving %.4f\n", 
         pt.toString(), minDist); 
       longiOut = minDistPoint.x; 
       latiOut = minDistPoint.y; 
       moved = 1;   
       pointsSnapped++; 
      } 
    //write a new row 

    String [] rowOut = {Double.toString(longiOut), Double.toString(latiOut), Integer.toString(moved), lineID}; 
    writer.writeNext(rowOut); 
     } 

     System.out.printf("Processed %d points (%.2f points per second). \n" 
       + "Snapped %d points.\n\n", 
       pointsProcessed, 
       1000.0 * pointsProcessed/elapsedTime, 
       pointsSnapped); 
     writer.close(); 
    } 
} 

我不仅是Java的新手,但只有自我训练的领域特定的语言,如R;我不是一个使用代码的编码人员,所以如果解决方案显而易见,我可能缺乏基本理论!

p.s我知道有更好的地图匹配解决方案(graphhopper等),我只是想开始eas!

谢谢!

回答

0

我会尽量避免走到JTS兔子洞的尽头,并坚持使用GeoTools(当然,我是GeoTools开发人员,所以我会这样说)。

首先,我会使用一个SpatialIndexFeatureCollection来保存我的行(假设它们适合内存,否则PostGIS表就是要走的路)。这节省了我不得不建立我自己的索引。

然后,我会使用CSVDataStore来保存从GPS流中解析我自己的点(因为我很懒,并且在那里也有很多错误)。

这意味着大部分工作归结为这个循环中,DWITHIN发现所有功能,在指定的距离:

try (SimpleFeatureIterator itr = pointFeatures.getFeatures().features()) { 
    while (itr.hasNext()) { 
    SimpleFeature f = itr.next(); 
    Geometry snapee = (Geometry) f.getDefaultGeometry(); 
    Filter filter = ECQL.toFilter("DWITH(\"the_geom\",'" + writer.write(snapee) + "'," + MAX_SEARCH_DISTANCE + ")"); 
    SimpleFeatureCollection possibles = indexed.subCollection(filter); 
    double minDist = Double.POSITIVE_INFINITY; 
    SimpleFeature bestFit = null; 
    Coordinate bestPoint = null; 
    try (SimpleFeatureIterator pItr = possibles.features()) { 
     while (pItr.hasNext()) { 
     SimpleFeature p = pItr.next(); 
     Geometry line = (Geometry) p.getDefaultGeometry(); 

     double dist = snapee.distance(line); 
     if (dist < minDist) { 
      minDist = dist; 
      bestPoint = DistanceOp.nearestPoints(snapee, line)[1]; 
      bestFit = p; 
     } 
     } 
    } 

在这个循环,你应该知道最近特征结束(BESTFIT) (包括其名称等),最近点(bestPoint)和移动距离(minDist)。

再次我可能会使用CSVDatastore写出功能。

如果你有几百万分,我可能会看看使用FilterFactory直接创建过滤器,而不是使用ECQL解析器。

+0

谢谢!这两方面都让我走到了我需要去的地方,让事情变得更加容易理解。 – GeoWork

0

基于在接受的答案中由iant提供的代码,我已经改变了我的代码,如下所示,我发布其他人使用Google搜索这类问题。

夫妇的事情:确保在ECQL解析器中有一个单元(我还没有尝试过FilterFactory的建议)。以下链接涵盖了CSVDataStore。请注意,默认情况下,经度和纬度分别硬编码为lonlathttp://docs.geotools.org/latest/userguide/tutorial/datastore/intro.html

package org.geotools.tutorial.quickstart; 

import java.io.*; 
import java.util.List; 
import java.util.Arrays; 

import com.vividsolutions.jts.geom.Coordinate; 
import com.vividsolutions.jts.geom.Envelope; 
import com.vividsolutions.jts.geom.Geometry; 
import com.vividsolutions.jts.geom.LineString; 
import com.vividsolutions.jts.geom.MultiLineString; 
import com.vividsolutions.jts.index.SpatialIndex; 
import com.vividsolutions.jts.index.strtree.STRtree; 
import com.vividsolutions.jts.linearref.LinearLocation; 
import com.vividsolutions.jts.linearref.LocationIndexedLine; 
import com.vividsolutions.jts.operation.distance.DistanceOp; 
import com.vividsolutions.jts.io.WKTWriter; 

import org.geotools.data.FeatureSource; 
import org.geotools.data.simple.SimpleFeatureSource; 
import org.geotools.data.simple.SimpleFeatureCollection; 
import org.geotools.data.simple.SimpleFeatureIterator; 
import org.geotools.data.FileDataStore; 
import org.geotools.data.FileDataStoreFinder; 
import org.geotools.feature.FeatureCollection; 
import org.geotools.geometry.jts.ReferencedEnvelope; 
import org.geotools.swing.data.JFileDataStoreChooser; 
import org.geotools.util.NullProgressListener; 
import org.geotools.data.collection.SpatialIndexFeatureCollection; 
import org.geotools.data.collection.SpatialIndexFeatureSource; 
import org.geotools.filter.text.ecql.ECQL; 

import org.opengis.filter.Filter; 
import org.opengis.feature.Feature; 
import org.opengis.feature.FeatureVisitor; 
import org.opengis.feature.simple.SimpleFeature; 
import com.opencsv.*; 
import com.csvreader.CsvReader; 




     public class SnapToLine { 

    public static void main(String[] args) throws Exception { 

     //input and output files and other parameters 
     String inputpoints = "/home/bitre/fakedata.csv"; 
     String outcsv = "fakedataOUT.csv"; 

     final double MAX_SEARCH_DISTANCE = 0.5; 


     /* 
     * Open a shapefile. You should choose one with line features 
     * (LineString or MultiLineString geometry) 
     * 
     */ 
     File file = JFileDataStoreChooser.showOpenFile("shp", null); 
     if (file == null) { 
      return; 
     } 

     FileDataStore store = FileDataStoreFinder.getDataStore(file); 
     SimpleFeatureSource source = store.getFeatureSource(); 

     // Check that we have line features 
     Class<?> geomBinding = source.getSchema().getGeometryDescriptor().getType().getBinding(); 
     boolean isLine = geomBinding != null 
       && (LineString.class.isAssignableFrom(geomBinding) || 
        MultiLineString.class.isAssignableFrom(geomBinding)); 

     if (!isLine) { 
      System.out.println("This example needs a shapefile with line features"); 
      return; 
     } 



     SimpleFeatureCollection features = source.getFeatures(); 

     SpatialIndexFeatureCollection indexed = new SpatialIndexFeatureCollection(features); 



/* 

/* 
     * We defined the maximum distance that a line can be from a point 
     * to be a candidate for snapping 
     */ 

     ReferencedEnvelope bounds = features.getBounds(); 




     //open up the CSVwriter 

     CSVWriter csvWriter = new CSVWriter(new FileWriter(outcsv)); 



     //CSVDataStore features for the points 


     CSVDataStore pointFeaturesCSV = new CSVDataStore(new File(inputpoints)); 
     String typeName = pointFeaturesCSV.getTypeNames()[0]; 
     SimpleFeatureSource pointFeatures = pointFeaturesCSV.getFeatureSource(typeName); 


     double longiOut; 
     double latiOut; 
     int progress = 0; 
     int remn; 
     String[] rowOut = new String[4]; 

       try (SimpleFeatureIterator itr = pointFeatures.getFeatures().features()) { 
      while (itr.hasNext()) { 
      SimpleFeature f = itr.next(); 
      Geometry snapee = (Geometry) f.getDefaultGeometry(); 
      WKTWriter writer = new WKTWriter(); 
      Filter filter = ECQL.toFilter("DWITHIN(\"the_geom\",'" + writer.write(snapee) + "'," + MAX_SEARCH_DISTANCE + "," + "kilometers" + ")"); 
      SimpleFeatureCollection possibles = indexed.subCollection(filter); 
      double minDist = Double.POSITIVE_INFINITY; 
      SimpleFeature bestFit = null; 
      Coordinate bestPoint = null; 
      try (SimpleFeatureIterator pItr = possibles.features()) { 
       while (pItr.hasNext()) { 
       SimpleFeature p = pItr.next(); 
       Geometry line = (Geometry) p.getDefaultGeometry(); 

       double dist = snapee.distance(line); 
       if (dist < minDist) { 
        minDist = dist; 
        bestPoint = DistanceOp.nearestPoints(snapee, line)[1]; // google DistanceOp 
        bestFit = p; 
       } 
       longiOut = bestPoint.x; 
       latiOut = bestPoint.y; 
       rowOut[0] = bestFit.getID(); 
       rowOut[1] = Double.toString(minDist); 
       rowOut[2] = Double.toString(longiOut); 
       rowOut[3] = Double.toString(latiOut); 

       //rowOut = {bestFit.getID(), Double.toString(minDist), Double.toString(longiOut), Double.toString(latiOut)}; 

       } 
       csvWriter.writeNext(rowOut); 
       progress ++; 
       remn = progress % 1000000; 
       if(remn == 0){ 
        System.out.println("Just snapped line" + progress); 
       } 

      } 

      } 


     } 
    } 
}