2017-01-10 9 views
1

Я пытаюсь написать программу Java, которая привяжет большую серию GPS-координат к файлу формы линии (дорожной сети) и вернет не только новый co -орданты, но уникальный идентификатор для отрезанного сегмента строки. Не имеет значения, является ли этот идентификатор FID, «индексом», который используется в других языках (например, где 1 является первой функцией и т. Д.) Или любым столбцом в таблице атрибутов.Java Geotools: линия привязки к линии, которая была привязана к

Я сделал это в R с помощью функции maptools::snapPointsToLines, но это не является масштабируемым, учитывая объемы данных мне нужно обрабатывать, так что я ищу для Java, чтобы обработать данные быстрее для анализа в R.

Мой код (ниже) в настоящее время очень похож на учебник geotools для привязки, с небольшими отличиями, которые я читал в (19 миллионов строк) CSV точек GPS вместо их создания, и я пишу CSV результатов. Он отлично подходит и намного быстрее, чем то, что я получаю, но я понятия не имею, как идентифицировать линию. Доступная документация, похоже, охватывает запросы и фильтрацию на наборах функций, которые я не могу сделать особенно применимыми к объекту строки индекса, который этот код создает, а существующая функция в моем коде toString() возвращает что-то непонятное для моих целей, например [email protected].

В принципе, я просто хочу, чтобы поле lineID создавало то, что любое другое программное обеспечение или язык ГИС может соответствовать определенному сегменту дороги.

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 и т. Д.), Я просто пытаюсь начать все!

Thankyou!

ответ

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 в принятом ответе, я изменил свой код следующим образом, который я отправляю для любого другого пользователя, задающего этот вопрос.

Пара вещей: убедитесь, что в модуле ECQL есть устройство (я еще не пробовал предложение FilterFactory). Номер CSVDataStore описан в следующей ссылке. Обратите внимание, что по умолчанию долгота и широта жестко закодированы как lon и lat соответственно. 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); 
       } 

      } 

      } 


     } 
    } 
} 

 Смежные вопросы

  • Нет связанных вопросов^_^