├── .gitignore ├── .travis.yml ├── src └── main │ └── java │ └── mapmatcher │ ├── HikingTrailMapMatcher.java │ └── MatchHikingTrail.java ├── pom.xml └── README.md /.gitignore: -------------------------------------------------------------------------------- 1 | /target 2 | .idea 3 | .mvn 4 | .classpath 5 | .project 6 | /.settings 7 | .Rhistory 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | language: java 3 | jdk: 4 | - oraclejdk7 5 | 6 | install: true 7 | 8 | env: 9 | - maven.wagon.http.ssl.insecure=true maven.wagon.http.ssl.allowall=true maven.wagon.http.ssl.ignore.validity.dates=true 10 | 11 | script: 12 | - mvn -X compile -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true 13 | -------------------------------------------------------------------------------- /src/main/java/mapmatcher/HikingTrailMapMatcher.java: -------------------------------------------------------------------------------- 1 | package mapmatcher; 2 | 3 | import fr.ign.cogit.geoxygene.api.feature.IFeature; 4 | import fr.ign.cogit.geoxygene.api.feature.IFeatureCollection; 5 | import fr.ign.cogit.geoxygene.contrib.cartetopo.Chargeur; 6 | import fr.ign.cogit.geoxygene.matching.hmmm.HMMMapMatcher; 7 | 8 | public class HikingTrailMapMatcher extends HMMMapMatcher { 9 | /** 10 | * @param gpsPop 11 | * @param networkPop 12 | * @param sigmaZ 13 | * @param selection 14 | * @param beta 15 | * @param distanceLimit 16 | */ 17 | public HikingTrailMapMatcher(IFeatureCollection gpsPop, 18 | IFeatureCollection networkPop, double sigmaZ, double selection, double beta, 19 | double distanceLimit) { 20 | super(gpsPop, networkPop, sigmaZ, selection, beta, distanceLimit); 21 | } 22 | 23 | @Override 24 | protected void importNetwork(IFeatureCollection network) { 25 | double tolerance = 0.1; 26 | Chargeur.importAsEdges(network, this.getNetworkMap(), null, null, null, null, null, tolerance); 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 3 | 4.0.0 4 | fr.ign 5 | mapmatcher 6 | 0.0.1-SNAPSHOT 7 | 8 | UTF-8 9 | 1.9-SNAPSHOT 10 | 11 | 12 | 13 | fr.ign.cogit 14 | geoxygene-matching 15 | ${geoxygene.noyau.version} 16 | 17 | 18 | 19 | 20 | cogit-snapshots 21 | IGN COGIT Snapshots 22 | https://forge-cogit.ign.fr/nexus/content/repositories/snapshots/ 23 | 24 | true 25 | 26 | 27 | false 28 | 29 | 30 | 31 | 32 | 33 | 34 | org.apache.maven.plugins 35 | maven-compiler-plugin 36 | 2.4 37 | 38 | 1.7 39 | 1.7 40 | 1.7 41 | 1.7 42 | 43 | **/* 44 | 45 | 46 | 47 | 48 | 49 | 50 | maven-assembly-plugin 51 | 52 | 53 | 54 | mapmatcher.MatchHikingTrail 55 | 56 | 57 | 58 | jar-with-dependencies 59 | 60 | 61 | 62 | 63 | make-assembly 64 | package 65 | 66 | single 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/julienperret/mapmatcher.svg?branch=master)](https://travis-ci.org/julienperret/mapmatcher) 2 | # Overview 3 | 4 | A simple **map matching** library using the *Hidden-Markov Model Map Matching algorithm* (HMM Map Matching) from Paul Newson and John Krumm, "Hidden Markov Map Matching Through Noise and Sparseness", 17th ACM SIGSPATIAL International Conference on Advances in Geographic Information Systems (ACM SIGSPATIAL GIS 2009), November 4-6, Seattle, WA, pp. 336-343. ([PDF](http://research.microsoft.com/en-us/um/people/jckrumm/Publications%202009/map%20matching%20ACM%20GIS%20camera%20ready.pdf), [slides](http://research.microsoft.com/en-us/um/people/jckrumm/Publications%202009/Hidden%20Markov%20Map%20Matching%20Through%20Noise%20and%20Sparseness%20-%20ACM%20SIGSPATIAL%202009-final.pptx), [shared data](http://research.microsoft.com/en-us/um/people/jckrumm/MapMatchingData/data.htm)). 5 | 6 | It is designed to be used from [R](https://www.r-project.org/) and uses the [GeOxygene](https://github.com/IGNF/geoxygene) implementation of the algorithm. 7 | 8 | # Usage 9 | ## Installation 10 | You will need to have [git](https://github.com/git/git) and [maven](https://github.com/apache/maven) installed to proceed. 11 | ~~~~ 12 | git clone git@github.com:julienperret/mapmatcher.git 13 | cd mapmatcher 14 | mvn install 15 | ~~~~ 16 | If that fails, it might be because of insecure SSL certificate. Try the following: 17 | ~~~~ 18 | mvn install -Dmaven.wagon.http.ssl.insecure=true -Dmaven.wagon.http.ssl.allowall=true 19 | ~~~~ 20 | this should produce a file named *mapmatcher-0.0.1-SNAPSHOT-jar-with-dependencies.jar* in the *target* directory. 21 | You can then include this library in *R* by placing the library in the *classpath*. 22 | ~~~~ 23 | export CLASSPATH=./target/mapmatcher-0.0.1-SNAPSHOT-jar-with-dependencies.jar 24 | ~~~~ 25 | 26 | Good, now you can use the library from *R*. In order to do that, let's create a simple function: 27 | ~~~~ 28 | matchHikingTrail<-function( 29 | gpsFile, 30 | networkFile, 31 | outFile=tempfile(), 32 | sigmaZ=10.0, 33 | selection=50.0, 34 | beta=6.0, 35 | distanceLimit=2000.0) 36 | { 37 | command=paste("java", 38 | "mapmatcher.MatchHikingTrail", 39 | gpsFile, 40 | networkFile, 41 | outFile, 42 | sigmaZ=10.0, 43 | selection=50.0, 44 | beta=6.0, 45 | distanceLimit) 46 | system(command) 47 | read.table(outFile,header=TRUE) 48 | } 49 | ~~~~ 50 | 51 | An example call of the function with existing shapefiles: 52 | ~~~~ 53 | matchHikingTrail("/my/existing/gps_file.shp","/my/existing/roadnetwork_file.shp","/output/matched_gps_file.csv") 54 | ~~~~ 55 | -------------------------------------------------------------------------------- /src/main/java/mapmatcher/MatchHikingTrail.java: -------------------------------------------------------------------------------- 1 | package mapmatcher; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.File; 5 | import java.io.FileWriter; 6 | import java.io.IOException; 7 | import java.util.ArrayList; 8 | import java.util.Arrays; 9 | import java.util.List; 10 | 11 | import org.apache.log4j.Level; 12 | import org.apache.log4j.Logger; 13 | 14 | import fr.ign.cogit.geoxygene.api.feature.IFeature; 15 | import fr.ign.cogit.geoxygene.api.feature.IPopulation; 16 | import fr.ign.cogit.geoxygene.api.spatial.coordgeom.ILineString; 17 | import fr.ign.cogit.geoxygene.api.spatial.geomprim.IPoint; 18 | import fr.ign.cogit.geoxygene.contrib.cartetopo.Arc; 19 | import fr.ign.cogit.geoxygene.feature.DefaultFeature; 20 | import fr.ign.cogit.geoxygene.feature.Population; 21 | import fr.ign.cogit.geoxygene.matching.hmmm.HMMMapMatcher; 22 | import fr.ign.cogit.geoxygene.matching.hmmm.HMMMapMatcher.Node; 23 | import fr.ign.cogit.geoxygene.schema.schemaConceptuelISOJeu.AttributeType; 24 | import fr.ign.cogit.geoxygene.schema.schemaConceptuelISOJeu.FeatureType; 25 | import fr.ign.cogit.geoxygene.spatial.coordgeom.GM_LineString; 26 | import fr.ign.cogit.geoxygene.spatial.geomprim.GM_Point; 27 | import fr.ign.cogit.geoxygene.util.algo.JtsAlgorithms; 28 | import fr.ign.cogit.geoxygene.util.conversion.ShapefileReader; 29 | import fr.ign.cogit.geoxygene.util.conversion.ShapefileWriter; 30 | 31 | public class MatchHikingTrail { 32 | 33 | public static void main(String[] args) throws IOException { 34 | Logger logger = Logger.getLogger(HMMMapMatcher.class.getName()); 35 | logger.setLevel(Level.FATAL); 36 | String gpsFile = args[0];// "/home/julien/test_mapmatching/20140716_3_test.shp" 37 | String networkFile = args[1];// "/home/julien/test_mapmatching/sentier_test.shp" 38 | String outFile = args[2]; 39 | 40 | // Le fichier des points matchés 41 | File file = new File(outFile); 42 | // if file doesnt exists, then create it 43 | if (!file.exists()) { 44 | file.createNewFile(); 45 | } 46 | FileWriter fw = new FileWriter(file.getAbsoluteFile()); 47 | BufferedWriter bw = new BufferedWriter(fw); 48 | bw.write("idgenerique idptini idsentier x y\n"); 49 | 50 | 51 | double sigmaZ = (args.length < 4) ? 10.0 : Double.parseDouble(args[3]);// 10.0 52 | double selection = (args.length < 5) ? 50.0 : Double.parseDouble(args[4]);// 50.0 53 | double beta = (args.length < 6) ? 6.0 : Double.parseDouble(args[5]);// 6.0 54 | double distanceLimit = (args.length < 7) ? 2000.0 : Double.parseDouble(args[6]);// 2000.0 55 | System.out.println("start"); 56 | IPopulation gpsPop = ShapefileReader.read(gpsFile, "traces", null, true); 57 | IPopulation networkPop = ShapefileReader.read(networkFile, "sentiers", null, true); 58 | System.out.println("loaded"); 59 | HikingTrailMapMatcher mapMatcher = new HikingTrailMapMatcher(gpsPop, networkPop, sigmaZ, selection, beta, distanceLimit); 60 | System.out.println("preprocess " + gpsPop.size()); 61 | // mapMatcher.preprocessPoints(); 62 | System.out.println("Map Matching start with " + gpsPop.size()); 63 | Node result = mapMatcher.computeTransitions(); 64 | System.out.println("Map Matching finished"); 65 | 66 | 67 | // Création du type géométrique 68 | FeatureType ftLines = new FeatureType(); 69 | ftLines.setTypeName("Traces"); 70 | ftLines.setNomClasse("DefaultFeature"); 71 | ftLines.setGeometryType(GM_LineString.class); 72 | Population popTraces = new Population(ftLines, false); // $NON-NLS-1$ 73 | popTraces.setClasse(DefaultFeature.class); 74 | for (int i = 0; i < gpsPop.size() - 1; i++) { 75 | IFeature p1 = gpsPop.get(i); 76 | IFeature p2 = gpsPop.get(i + 1); 77 | popTraces.nouvelElement(new GM_LineString(Arrays.asList(p1.getGeom().centroid(), p2.getGeom().centroid()))); 78 | } 79 | // RECALAGE DES POINTS GPS SUR LE RESEAU 80 | System.out.println("Traces wrote"); 81 | // Création du type géométrique 82 | FeatureType ftPoints = new FeatureType(); 83 | ftPoints.setGeometryType(GM_Point.class); 84 | ftPoints.addFeatureAttribute(new AttributeType("id", "int")); 85 | // L'objet inséré à la fin dans outfile : 86 | Population popMatchedPoints = new Population("Points Recales"); //$NON-NLS-1$ 87 | popMatchedPoints.setFeatureType(ftPoints); 88 | popMatchedPoints.setClasse(DefaultFeature.class); 89 | 90 | FeatureType ftVector = new FeatureType(); 91 | ftVector.setTypeName("Vector"); 92 | // ftLines.setNomClasse("Arc"); 93 | ftVector.setGeometryType(GM_LineString.class); 94 | // ftVector.addFeatureAttribute(new AttributeType("id", "int")); 95 | ftVector.addFeatureAttribute(new AttributeType("Poids", "double")); 96 | Population popMatchedVectors = new Population("Vectors"); //$NON-NLS-1$ 97 | popMatchedVectors.setClasse(Arc.class); 98 | popMatchedVectors.setFeatureType(ftVector); 99 | List lengths = new ArrayList<>(); 100 | for (int i = 0; i < gpsPop.size(); i++) { 101 | GM_Point p = (GM_Point) gpsPop.get(i).getGeom(); 102 | ILineString l = result.getStates().get(i).getGeometrie(); 103 | DefaultFeature projectedPoint = popMatchedPoints.nouvelElement(); 104 | IPoint projection = JtsAlgorithms.getClosestPoint(p.getPosition(), l).toGM_Point(); 105 | projectedPoint.setGeom(projection); 106 | projectedPoint.setId(i); 107 | gpsPop.get(i).setId(i); 108 | ILineString line = new GM_LineString(p.getPosition(), projection.getPosition()); 109 | Arc edge = popMatchedVectors.nouvelElement(line); //popMatchedVectors 110 | edge.setId(i); 111 | double length = line.length(); 112 | edge.setPoids(length); 113 | lengths.add(length); 114 | 115 | // Ecriture du fichier des points matchés 116 | IPoint pmatche = (IPoint) popMatchedPoints.get(i).getGeom(); 117 | bw.write(i 118 | + " " + gpsPop.get(i).getAttribute("idp") 119 | + " " + result.getStates().get(i).getCorrespondant(0).getAttribute("SL_ID") 120 | + " " + pmatche.getPosition().getX() 121 | + " " + pmatche.getPosition().getY() + "\n"); 122 | } 123 | 124 | bw.close(); 125 | fw.close(); 126 | 127 | /* File file = new File(outFile); 128 | // if file doesnt exists, then create it 129 | if (!file.exists()) { 130 | file.createNewFile(); 131 | } 132 | FileWriter fw = new FileWriter(file.getAbsoluteFile()); 133 | BufferedWriter bw = new BufferedWriter(fw); 134 | bw.write("idgenerique idptini idsentier x y\n"); 135 | for (IFeature f : popMatchedPoints) { 136 | IPoint p = (IPoint) f.getGeom(); 137 | bw.write(f.getId() + " " + + " " + + " " + p.getPosition().getX() + " " + p.getPosition().getY() + "\n"); 138 | } 139 | bw.close(); 140 | fw.close(); 141 | */ 142 | 143 | ShapefileWriter.write(popMatchedVectors, gpsFile.substring(0, gpsFile.lastIndexOf("."))+"_vectors.shp"); 144 | 145 | // System.out.println("length"); 146 | // for (Double d : lengths) System.out.println("" + d); 147 | } 148 | } 149 | --------------------------------------------------------------------------------