├── .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 extends IFeature> gpsPop,
18 | IFeatureCollection extends IFeature> 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 extends IFeature> 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 | [](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 |
--------------------------------------------------------------------------------