Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Bbox #421

Merged
merged 6 commits into from
Jun 14, 2019
Merged

Bbox #421

Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ Photon software is open source and licensed under [Apache License, Version 2.0](
- location bias
- typo tolerance
- filter by osm tag and value
- filter by bounding box
- reverse geocode a coordinate to an address
- OSM data import (built upon [Nominatim](https://github.com/twain47/Nominatim)) inclusive continuous updates

Expand Down Expand Up @@ -134,6 +135,12 @@ http://localhost:2322/api?q=berlin&limit=2
http://localhost:2322/api?q=berlin&lang=it
```

#### Filter results by bounding box
Expected format is minLon,minLat,maxLon,maxLat.
```
http://localhost:2322/api?q=berlin&bbox=9.5,51.5,11.5,53.5
```

#### Filter results by [tags and values](http://taginfo.openstreetmap.org/projects/nominatim#tags)
*Note: not all tags on [link in the title](http://taginfo.openstreetmap.org/projects/nominatim#tags) are supported. Please see [nominatim source](https://github.com/openstreetmap/osm2pgsql/blob/master/output-gazetteer.cpp#L81) for an accurate list.*
If one or many query parameters named ```osm_tag``` are present, photon will attempt to filter results by those tags. In general, here is the expected format (syntax) for the value of osm_tag request parameters.
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package de.komoot.photon.query;

import com.vividsolutions.jts.geom.Envelope;

import de.komoot.photon.utils.Function;
import spark.Request;

/**
* Converts the bbox parameter into an Envelope and performs format checking.
* Created by Holger Bruch on 10/13/2018.
*/
public class BoundingBoxParamConverter implements Function<Request, Envelope, BadRequestException> {

public static final String INVALID_BBOX_ERROR_MESSAGE = "invalid number of supplied coordinates for parameter 'bbox', expected format is: minLon,minLat,maxLon,maxLat";
public static final String INVALID_BBOX_BOUNDS_MESSAGE = "Invalid bounds for parameter bbox, expected values minLat, maxLat element [-90,90], minLon, maxLon element [-180,180]";

@Override
public Envelope apply(Request webRequest) throws BadRequestException {
String bboxParam = webRequest.queryParams("bbox");
if (bboxParam == null) {
return null;
}
Envelope bbox = null;
try {
String[] bboxCoords = bboxParam.split(",");
if (bboxCoords.length != 4) {
throw new BadRequestException(400, INVALID_BBOX_ERROR_MESSAGE);
}
Double minLon = Double.valueOf(bboxCoords[0]);
Double minLat = Double.valueOf(bboxCoords[1]);
Double maxLon = Double.valueOf(bboxCoords[2]);
Double maxLat = Double.valueOf(bboxCoords[3]);
if (minLon > 180.0 || minLon < -180.00 || maxLon > 180.0 || maxLon < -180.00) {
throw new BadRequestException(400, INVALID_BBOX_BOUNDS_MESSAGE);
}
if (minLat > 90.0 || minLat < -90.00 || maxLat > 90.0 || maxLat < -90.00) {
throw new BadRequestException(400, INVALID_BBOX_BOUNDS_MESSAGE);
}
bbox = new Envelope(minLon, maxLon, minLat, maxLat);
} catch (NumberFormatException nfe) {
throw new BadRequestException(400, INVALID_BBOX_ERROR_MESSAGE);
}

return bbox;
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.komoot.photon.query;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Point;

import java.util.HashMap;
Expand All @@ -20,8 +21,8 @@ public class FilteredPhotonRequest extends PhotonRequest {
private Map<String, Set<String>> excludeTags = new HashMap<String, Set<String>>(3);
private Map<String, Set<String>> excludeTagValues = new HashMap<String, Set<String>>(3);

FilteredPhotonRequest(String query, int limit, Point locationForBias, double locBiasScale, String language) {
super(query, limit, locationForBias, locBiasScale, language);
FilteredPhotonRequest(String query, int limit, Envelope bbox, Point locationForBias, double locBiasScale, String language) {
super(query, limit, bbox, locationForBias, locBiasScale, language);
}

public Set<String> keys() {
Expand Down
15 changes: 15 additions & 0 deletions src/main/java/de/komoot/photon/query/PhotonQueryBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@


import com.google.common.collect.ImmutableSet;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Point;
import org.elasticsearch.common.lucene.search.function.CombineFunction;
import org.elasticsearch.common.lucene.search.function.FiltersFunctionScoreQuery.ScoreMode;
Expand Down Expand Up @@ -51,6 +52,8 @@ public class PhotonQueryBuilder implements TagFilterQueryBuilder {

private MatchQueryBuilder languageMatchQueryBuilder;

private GeoBoundingBoxQueryBuilder bboxQueryBuilder;

private QueryBuilder m_finalQueryBuilder;

protected ArrayList<FilterFunctionBuilder> m_alFilterFunction4QueryBuilder = new ArrayList<>(1);
Expand Down Expand Up @@ -133,6 +136,15 @@ public TagFilterQueryBuilder withLocationBias(Point point, double scale) {
.boostMode(CombineFunction.MULTIPLY);
return this;
}

@Override
public TagFilterQueryBuilder withBoundingBox(Envelope bbox) {
if (bbox == null) return this;
bboxQueryBuilder = new GeoBoundingBoxQueryBuilder("coordinate");
bboxQueryBuilder.setCorners(bbox.getMaxY(), bbox.getMinX(), bbox.getMinY(), bbox.getMaxX());

return this;
}

@Override
public TagFilterQueryBuilder withTags(Map<String, Set<String>> tags) {
Expand Down Expand Up @@ -314,6 +326,9 @@ public QueryBuilder buildQuery() {
m_queryBuilderForTopLevelFilter.must(andQueryBuilderForExcludeTagFiltering);

}

if (bboxQueryBuilder != null)
m_queryBuilderForTopLevelFilter.filter(bboxQueryBuilder);

state = State.FINISHED;

Expand Down
9 changes: 8 additions & 1 deletion src/main/java/de/komoot/photon/query/PhotonRequest.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.komoot.photon.query;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Point;

import java.io.Serializable;
Expand All @@ -13,13 +14,15 @@ public class PhotonRequest implements Serializable {
private Point locationForBias;
private String language;
private final double scale;
private Envelope bbox;

public PhotonRequest(String query, int limit, Point locationForBias, double scale, String language) {
public PhotonRequest(String query, int limit, Envelope bbox, Point locationForBias, double scale, String language) {
this.query = query;
this.limit = limit;
this.locationForBias = locationForBias;
this.scale = scale;
this.language = language;
this.bbox = bbox;
}

public String getQuery() {
Expand All @@ -29,6 +32,10 @@ public String getQuery() {
public Integer getLimit() {
return limit;
}

public Envelope getBbox() {
return bbox;
}

public Point getLocationForBias() {
return locationForBias;
Expand Down
13 changes: 9 additions & 4 deletions src/main/java/de/komoot/photon/query/PhotonRequestFactory.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package de.komoot.photon.query;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Point;
import spark.QueryParamsMap;
import spark.Request;
Expand All @@ -15,12 +17,13 @@
public class PhotonRequestFactory {
private final LanguageChecker languageChecker;
private final static LocationParamConverter optionalLocationParamConverter = new LocationParamConverter(false);
private final BoundingBoxParamConverter bboxParamConverter;

protected static HashSet<String> m_hsRequestQueryParams = new HashSet<>(Arrays.asList("lang", "q", "lon", "lat",
"limit", "osm_tag", "location_bias_scale", "debug"));

"limit", "osm_tag", "location_bias_scale", "bbox", "debug"));
public PhotonRequestFactory(Set<String> supportedLanguages) {
this.languageChecker = new LanguageChecker(supportedLanguages);
this.bboxParamConverter = new BoundingBoxParamConverter();
}

public <R extends PhotonRequest> R create(Request webRequest) throws BadRequestException {
Expand All @@ -42,7 +45,9 @@ public <R extends PhotonRequest> R create(Request webRequest) throws BadRequestE
} catch (NumberFormatException e) {
limit = 15;
}

Point locationForBias = optionalLocationParamConverter.apply(webRequest);
Envelope bbox = bboxParamConverter.apply(webRequest);

// don't use too high default value, see #306
double scale = 1.6;
Expand All @@ -56,9 +61,9 @@ public <R extends PhotonRequest> R create(Request webRequest) throws BadRequestE

QueryParamsMap tagFiltersQueryMap = webRequest.queryMap("osm_tag");
if (!new CheckIfFilteredRequest().execute(tagFiltersQueryMap)) {
return (R) new PhotonRequest(query, limit, locationForBias, scale, language);
return (R) new PhotonRequest(query, limit, bbox, locationForBias, scale, language);
}
FilteredPhotonRequest photonRequest = new FilteredPhotonRequest(query, limit, locationForBias, scale, language);
FilteredPhotonRequest photonRequest = new FilteredPhotonRequest(query, limit, bbox, locationForBias, scale, language);
String[] tagFilters = tagFiltersQueryMap.values();
setUpTagFilters(photonRequest, tagFilters);

Expand Down
6 changes: 6 additions & 0 deletions src/main/java/de/komoot/photon/query/ReverseQueryBuilder.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package de.komoot.photon.query;

import com.google.common.collect.ImmutableSet;
import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Point;
import org.elasticsearch.common.unit.DistanceUnit;
import org.elasticsearch.index.query.BoolQueryBuilder;
Expand Down Expand Up @@ -45,6 +46,11 @@ public TagFilterQueryBuilder withLocationBias(Point point, double scale) {
throw new RuntimeException(new NoSuchMethodException("this method is not implemented (NOOP)"));
}

@Override
public TagFilterQueryBuilder withBoundingBox(Envelope bbox) {
throw new RuntimeException(new NoSuchMethodException("this method is not implemented (NOOP)"));
}

@Override
public TagFilterQueryBuilder withTags(Map<String, Set<String>> tags) {
throw new RuntimeException(new NoSuchMethodException("this method is not implemented (NOOP)"));
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.komoot.photon.query;

import com.vividsolutions.jts.geom.Envelope;
import com.vividsolutions.jts.geom.Point;
import org.elasticsearch.index.query.QueryBuilder;

Expand All @@ -21,6 +22,13 @@ public interface TagFilterQueryBuilder {
*/
TagFilterQueryBuilder withLimit(Integer limit);

/**
* Search results will be filtered to places contained in the bounding box (WGS84) provided in the argument.
*
* @param bbox Geographical {@link Envelope}
*/
TagFilterQueryBuilder withBoundingBox(Envelope bbox);

/**
* Location bias for query. By setting this, places found will be sorted by proximity to this point.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,8 @@ public TagFilterQueryBuilder buildQuery(FilteredPhotonRequest photonRequest) {
withoutKeys(excludeKeys).
withoutValues(excludeValues).
withTagsNotValues(excludeTagValues).
withLocationBias(photonRequest.getLocationForBias(), photonRequest.getScaleForBias());
withLocationBias(photonRequest.getLocationForBias(), photonRequest.getScaleForBias()).
withBoundingBox(photonRequest.getBbox());
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ public SimplePhotonRequestHandler(ElasticsearchSearcher elasticsearchSearcher) {
@Override
public TagFilterQueryBuilder buildQuery(PhotonRequest photonRequest) {
return PhotonQueryBuilder.builder(photonRequest.getQuery(), photonRequest.getLanguage()).
withLocationBias(photonRequest.getLocationForBias(), photonRequest.getScaleForBias());
withLocationBias(photonRequest.getLocationForBias(), photonRequest.getScaleForBias()).
withBoundingBox(photonRequest.getBbox());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class FilteredPhotonRequestTest {

@Test
public void testNotKey() {
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, 0, null);
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, null, 0, null);
filteredPhotonRequest.notKeys("exclude");
filteredPhotonRequest.notKeys("exclude");
filteredPhotonRequest.notKeys("anotherExclude");
Expand All @@ -27,7 +27,7 @@ public void testNotKey() {

@Test
public void testNotTag() {
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, 0, null);
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, null, 0, null);
filteredPhotonRequest.notTags("aKey", ImmutableSet.of("aValue"));
filteredPhotonRequest.notTags("anotherKey", ImmutableSet.of("anotherValue"));
Map<String, Set<String>> excludeTags = filteredPhotonRequest.notTags();
Expand All @@ -37,7 +37,7 @@ public void testNotTag() {

@Test
public void testNotValue() {
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, 0, null);
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, null, 0, null);
filteredPhotonRequest.notValues("exclude");
filteredPhotonRequest.notValues("exclude");
filteredPhotonRequest.notValues("anotherExclude");
Expand All @@ -48,7 +48,7 @@ public void testNotValue() {

@Test
public void testKey() {
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, 0, null);
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, null, 0, null);
filteredPhotonRequest.keys("keyToInclude");
filteredPhotonRequest.keys("keyToInclude");
filteredPhotonRequest.keys("anotherKeyToInclude");
Expand All @@ -57,7 +57,7 @@ public void testKey() {

@Test
public void testTag() {
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, 0, null);
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, null, 0, null);
filteredPhotonRequest.tags("aKey", ImmutableSet.of("aValue"));
filteredPhotonRequest.tags("anotherKey", ImmutableSet.of("anotherValue"));
Map<String, Set<String>> includeTags = filteredPhotonRequest.tags();
Expand All @@ -68,7 +68,7 @@ public void testTag() {

@Test
public void testValue() {
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, 0, null);
FilteredPhotonRequest filteredPhotonRequest = new FilteredPhotonRequest(null, 0, null, null, 0, null);
filteredPhotonRequest.values("keyToInclude");
filteredPhotonRequest.values("keyToInclude");
filteredPhotonRequest.values("anotherKeyToInclude");
Expand Down
Loading