2017-01-10 6 views
1

大規模な一連のGPS座標をラインシェイプファイル(道路ネットワーク)にスナップして新しいコー-ordinatesではなく、スナップされた線分の一意の識別子です。この識別子が他の言語で使用されているようなFID、「インデックス」(つまり、1が最初のフィーチャなど)または属性テーブルの任意の列であるかどうかは関係ありません。Java Geotools:スナップインされたライン識別スナップショット

私はmaptools::snapPointsToLines機能を使用してRでこれを行っているが、これは私が処理する必要があるデータの量与えられたスケーラブルではありませんので、Javaは

R.に分析するために、より迅速にデータを処理するために私が探しています

私のコード(下)は現在、ジオツールのチュートリアルと非常によく似ていますが、GPSポイントを生成する代わりに(1900万行の)CSVで読み込んだマイナーな違いがあり、結果のCSVを書きます。それは、私が得ていたよりもはるかに素早く、スナップしますが、私はどのようにスナップされた線を特定するか分かりません。使用可能なドキュメントはquerysをカバーし、特にこのコードが作成する索引行オブジェクトに適用できないフィーチャセットをフィルタリングするように見えます。toString()は私の目的にはわからない何かを返します。例えばcom.vividsolutions.jts.linearreff.LocationIndexedLi[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私は、より良いマップマッチングソリューションがあることを知っています(グラフホッパーなど)、私は簡単に始めようとしています!

ありがとうございました!

答えて

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))の行(そのID &の名前を含む)から、

もう一度、私はおそらくCSVDatastoreを使って機能を取り返します。

何百万という点がある場合は、おそらくFilterFactoryを使用して、ECQLパーサを使用せずにフィルタを直接作成することを検討してください。

+0

ありがとう!これは、私が行く必要のある場所に私を連れてきて、物事をかなり分かりやすくしました。 – GeoWork

0

受け入れられた回答のiantが提供するコードに基づいて、以下のように私のコードを変更しました。このコードは、この種の質問をしている他の人のために投稿します。

いくつかのもの:ECQLパーサーにユニットがあることを確認してください(私はまだFilterFactoryの提案を試していません)。 CSVDataStoreについては、次のリンクを参照してください。デフォルトでは経度と緯度はそれぞれlonlatにハードコードされています。 http://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); 
       } 

      } 

      } 


     } 
    } 
} 
関連する問題