Skip to content

Commit

Permalink
Merge pull request #1741 from laurentg/bano-geocoder
Browse files Browse the repository at this point in the history
Bano geocoder
  • Loading branch information
abyrd committed Feb 10, 2015
2 parents 9a92edd + be5e660 commit 8ae25c6
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 0 deletions.
5 changes: 5 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -430,6 +430,11 @@
<artifactId>gt-epsg-hsql</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>de.grundid.opendatalab</groupId>
<artifactId>geojson-jackson</artifactId>
<version>1.2</version>
</dependency>

<!-- TESTING -->
<dependency>
Expand Down
110 changes: 110 additions & 0 deletions src/main/java/org/opentripplanner/geocoder/bano/BanoGeocoder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */

package org.opentripplanner.geocoder.bano;

import java.io.IOException;
import java.io.InputStream;
import java.net.URI;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.List;

import javax.ws.rs.core.UriBuilder;

import org.geojson.Feature;
import org.geojson.FeatureCollection;
import org.geojson.GeoJsonObject;
import org.geojson.Point;
import org.opentripplanner.geocoder.Geocoder;
import org.opentripplanner.geocoder.GeocoderResult;
import org.opentripplanner.geocoder.GeocoderResults;

import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.vividsolutions.jts.geom.Envelope;

/**
* A geocoder using the data.gouv.fr API of BANO (Base Nationale d'Adresse Ouverte), the official
* open-data address source covering the whole of France.
*
* The returned data is rather simple to use, as it returns a GeoJSON features collection.
*
* Obviously, this geocoder will only work in France.
*
* @author laurent
*/
public class BanoGeocoder implements Geocoder {

private static final String BANO_URL = "http://api.adresse.data.gouv.fr/search/";

private static final int CLAMP_RESULTS = 10;

private ObjectMapper mapper;

public BanoGeocoder() {
mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
}

/**
*/
@Override
public GeocoderResults geocode(String address, Envelope bbox) {

try {
URL banoUrl = getBanoGeocoderUrl(address, bbox);
URLConnection conn = banoUrl.openConnection();
InputStream in = conn.getInputStream();
FeatureCollection featureCollection = mapper.readValue(in, FeatureCollection.class);
in.close();

List<GeocoderResult> geocoderResults = new ArrayList<GeocoderResult>();
for (Feature feature : featureCollection.getFeatures()) {
GeoJsonObject geom = feature.getGeometry();
if (geom instanceof Point) {
Point p = (Point) geom;
GeocoderResult res = new GeocoderResult();
res.setLat(p.getCoordinates().getLatitude());
res.setLng(p.getCoordinates().getLongitude());
res.setDescription(feature.getProperties().get("label").toString());
/*
* Note: We also have here as properties a break-down of other details, such as
* the house number, street, city, postcode... Can be useful if needed.
*/
geocoderResults.add(res);
} else {
// Should not happen according to the API
}
}
return new GeocoderResults(geocoderResults);

} catch (IOException e) {
e.printStackTrace();
return new GeocoderResults(e.getLocalizedMessage());
}
}

private URL getBanoGeocoderUrl(String address, Envelope bbox) throws IOException {
UriBuilder uriBuilder = UriBuilder.fromUri(BANO_URL);
uriBuilder.queryParam("q", address);
uriBuilder.queryParam("limit", CLAMP_RESULTS);
if (bbox != null) {
uriBuilder.queryParam("lat", bbox.centre().y);
uriBuilder.queryParam("lon", bbox.centre().x);
}
URI uri = uriBuilder.build();
return new URL(uri.toString());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/* This program is free software: you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public License
as published by the Free Software Foundation, either version 3 of
the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>. */

package org.opentripplanner.geocoder.bano;

import org.junit.Test;
import org.opentripplanner.common.geometry.SphericalDistanceLibrary;
import org.opentripplanner.geocoder.GeocoderResult;
import org.opentripplanner.geocoder.GeocoderResults;

import com.vividsolutions.jts.geom.Envelope;

public class BanoGeocoderTest {

/**
* TODO -- This unit-test rely on an on-line API to be up and running, which may not be the case
* if a network connection is not active or the server is down.
*/
@Test
public void testOnLine() throws Exception {

BanoGeocoder banoGeocoder = new BanoGeocoder();
// The Presidential palace of the French Republic is not supposed to move often
Envelope bbox = new Envelope();
bbox.expandToInclude(2.25, 48.8);
bbox.expandToInclude(2.35, 48.9);
GeocoderResults results = banoGeocoder.geocode("55 Rue du Faubourg Saint-Honoré", bbox);

assert (results.getResults().size() >= 1);

boolean found = false;
for (GeocoderResult result : results.getResults()) {
if ("55 Rue du Faubourg Saint-Honoré 75008 Paris".equals(result.getDescription())) {
double dist = SphericalDistanceLibrary.getInstance().distance(result.getLat(),
result.getLng(), 48.870637, 2.316939);
assert (dist < 100);
found = true;
}
}
assert (found);

}

}

0 comments on commit 8ae25c6

Please sign in to comment.