Skip to content

Commit

Permalink
[EAT-101] Google Location search uses map bbox for more accurate results
Browse files Browse the repository at this point in the history
v2.1.4
- Fixed "Premature end of chunk" exception when URLCache request a URL
- Better logging when search API key is missing
- Added "bounds" parameter to Google location search
  • Loading branch information
gaellafond committed Oct 22, 2020
1 parent 1794997 commit e4c0e06
Show file tree
Hide file tree
Showing 6 changed files with 163 additions and 31 deletions.
20 changes: 19 additions & 1 deletion clientResources/amc/modules/MapPanel/Layer/SearchResults.js
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,10 @@ Atlas.Layer.SearchResults = OpenLayers.Class(Atlas.Layer.AbstractLayer, {
return;
}

this.mapPanel = mapPanel;
this.json = jsonLayer;
this.parent = parent;

this.searchCount = Atlas.Layer.SearchResults.count;
Atlas.Layer.SearchResults.count++;

Expand Down Expand Up @@ -161,12 +165,26 @@ Atlas.Layer.SearchResults = OpenLayers.Class(Atlas.Layer.AbstractLayer, {
var params = {
client: Atlas.conf['clientId'],
query: this.query,
bounds: null,
offset: this.page * this.NB_RESULTS_PER_PAGE,
qty: this.NB_RESULTS_PER_PAGE,
noCache: (new Date()).getTime() // Anti-caching
};

// Add map bounds to the request
if (this.mapPanel && this.mapPanel.map) {
var mapBounds = this.mapPanel.map.calculateBounds();
if (mapBounds) {
var reprojectedBounds = mapBounds.transform(this.mapPanel.map.getProjectionObject(), this.mapPanel.defaultLonLatProjection);

if (reprojectedBounds) {
params["north"] = reprojectedBounds.top;
params["east"] = reprojectedBounds.right;
params["south"] = reprojectedBounds.bottom;
params["west"] = reprojectedBounds.left;
}
}
}

OpenLayers.Request.GET({
url: this.searchServiceUrl,
params: params,
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
<groupId>au.gov.aims</groupId>
<artifactId>atlasmapper</artifactId>
<packaging>war</packaging>
<version>2.1.3</version>
<version>2.1.4</version>
<name>AtlasMapper server and clients</name>
<description>This application compiled as a single War, that can be deployed in Tomcat, without any other dependency.\n\
It contains:\n\
Expand Down
51 changes: 37 additions & 14 deletions src/main/java/au/gov/aims/atlasmapperserver/ClientConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -1260,8 +1260,13 @@ private String getHTMLListAndHideChildrenLayers(JSONObject layers, JSONArray chi
}


public JSONObject locationSearch(URLCache urlCache, String query, String referer, String mapBounds, int offset, int qty)
throws JSONException, IOException, RevivableThreadInterruptedException {
public JSONObject locationSearch(
URLCache urlCache,
String query,
String referer,
LocationSearch.LocationSearchBoundingBox mapBounds,
int offset, int qty
) throws JSONException, IOException, RevivableThreadInterruptedException {

if (Utils.isBlank(query) || qty <= 0) {
return null;
Expand Down Expand Up @@ -1293,10 +1298,16 @@ public int compare(JSONObject o1, JSONObject o2) {

try {
String googleSearchAPIKey = this.getGoogleSearchAPIKey();
if (this.isShowGoogleResults() && Utils.isNotBlank(googleSearchAPIKey)) {
List<JSONObject> googleResults = LocationSearch.googleSearch(urlCache, googleSearchAPIKey, referer, encodedQuery, mapBounds);
if (googleResults != null && !googleResults.isEmpty()) {
resultsSet.addAll(googleResults);
if (this.isShowGoogleResults()) {
if (Utils.isBlank(googleSearchAPIKey)) {
LOGGER.warning(String.format("Client %s can't use Google Search. The Google search API key is blank.",
this.getClientId()));
} else {
List<JSONObject> googleResults = LocationSearch.googleSearch(
urlCache, googleSearchAPIKey, referer, encodedQuery, mapBounds);
if (googleResults != null && !googleResults.isEmpty()) {
resultsSet.addAll(googleResults);
}
}
}
} catch(Exception ex) {
Expand All @@ -1306,10 +1317,16 @@ public int compare(JSONObject o1, JSONObject o2) {

try {
String osmSearchAPIKey = this.getOsmSearchAPIKey();
if (this.isShowOSMResults() && Utils.isNotBlank(osmSearchAPIKey)) {
List<JSONObject> osmNominatimResults = LocationSearch.osmNominatimSearch(urlCache, osmSearchAPIKey, referer, encodedQuery, mapBounds);
if (osmNominatimResults != null && !osmNominatimResults.isEmpty()) {
resultsSet.addAll(osmNominatimResults);
if (this.isShowOSMResults()) {
if (Utils.isBlank(osmSearchAPIKey)) {
LOGGER.warning(String.format("Client %s can't use OSM Search. The OSM search API key is blank.",
this.getClientId()));
} else {
List<JSONObject> osmNominatimResults = LocationSearch.osmNominatimSearch(
urlCache, osmSearchAPIKey, referer, encodedQuery, mapBounds);
if (osmNominatimResults != null && !osmNominatimResults.isEmpty()) {
resultsSet.addAll(osmNominatimResults);
}
}
}
} catch(Exception ex) {
Expand All @@ -1319,10 +1336,16 @@ public int compare(JSONObject o1, JSONObject o2) {

try {
String arcGISSearchUrl = this.getArcGISSearchUrl();
if (this.isShowArcGISResults() && Utils.isNotBlank(arcGISSearchUrl)) {
List<JSONObject> arcGISResults = LocationSearch.arcGISSearch(urlCache, referer, arcGISSearchUrl, encodedQuery, mapBounds);
if (arcGISResults != null && !arcGISResults.isEmpty()) {
resultsSet.addAll(arcGISResults);
if (this.isShowArcGISResults()) {
if (Utils.isBlank(arcGISSearchUrl)) {
LOGGER.warning(String.format("Client %s can't use ArcGIS Search. The ArcGIS search URL is blank.",
this.getClientId()));
} else {
List<JSONObject> arcGISResults = LocationSearch.arcGISSearch(
urlCache, referer, arcGISSearchUrl, encodedQuery, mapBounds);
if (arcGISResults != null && !arcGISResults.isEmpty()) {
resultsSet.addAll(arcGISResults);
}
}
}
} catch(Exception ex) {
Expand Down
92 changes: 84 additions & 8 deletions src/main/java/au/gov/aims/atlasmapperserver/LocationSearch.java
Original file line number Diff line number Diff line change
Expand Up @@ -62,16 +62,37 @@ private static void incSearchCount() {
// Google
// API: https://developers.google.com/maps/documentation/geocoding/
// URL: http://maps.googleapis.com/maps/api/geocode/json?address={QUERY}&sensor=false
public static List<JSONObject> googleSearch(URLCache urlCache, String googleSearchAPIKey, String referer, String encodedQuery, String mapBounds)
throws JSONException, IOException, URISyntaxException, SQLException, RevivableThreadInterruptedException, ClassNotFoundException {

String googleSearchUrl = "https://maps.googleapis.com/maps/api/geocode/json?address={QUERY}&sensor=false&key={APIKEY}";
public static List<JSONObject> googleSearch(
URLCache urlCache,
String googleSearchAPIKey,
String referer,
String encodedQuery,
LocationSearch.LocationSearchBoundingBox mapBounds
) throws JSONException, IOException, URISyntaxException, SQLException, RevivableThreadInterruptedException, ClassNotFoundException {

// The bounds parameter defines the latitude/longitude coordinates of
// the southwest and northeast corners of this bounding box
// using a pipe (|) character to separate the coordinates.
// NOTE: The pipe needs to be URL encoded: | = %7C
String googleSearchUrl = "https://maps.googleapis.com/maps/api/geocode/json?" +
"address={QUERY}" +
"&sensor=false" +
"&key={APIKEY}" +
"&bounds={SOUTH},{WEST}%7C{NORTH},{EAST}";

String encodedGoogleSearchAPIKey = URLEncoder.encode(googleSearchAPIKey.trim(), "UTF-8");
String queryURLStr = googleSearchUrl
.replace("{QUERY}", encodedQuery)
.replace("{APIKEY}", encodedGoogleSearchAPIKey);

if (mapBounds != null) {
queryURLStr = queryURLStr
.replace("{SOUTH}", "" + mapBounds.getSouth())
.replace("{WEST}", "" + mapBounds.getWest())
.replace("{NORTH}", "" + mapBounds.getNorth())
.replace("{EAST}", "" + mapBounds.getEast());
}

JSONObject json = LocationSearch.getSearchJSONObjectResponse(urlCache, queryURLStr, referer);
if (json == null) {
return null;
Expand Down Expand Up @@ -256,8 +277,13 @@ public static List<JSONObject> osmSearch(URLCache urlCache, String osmSearchAPIK
// OSM Nominatim
// API: https://developer.mapquest.com/documentation/open/nominatim-search/
// URL: http://open.mapquestapi.com/nominatim/v1/search?format=json&q={QUERY}
public static List<JSONObject> osmNominatimSearch(URLCache urlCache, String osmSearchAPIKey, String referer, String encodedQuery, String mapBounds)
throws JSONException, IOException, URISyntaxException, SQLException, RevivableThreadInterruptedException, ClassNotFoundException {
public static List<JSONObject> osmNominatimSearch(
URLCache urlCache,
String osmSearchAPIKey,
String referer,
String encodedQuery,
LocationSearch.LocationSearchBoundingBox mapBounds
) throws JSONException, IOException, URISyntaxException, SQLException, RevivableThreadInterruptedException, ClassNotFoundException {

String osmSearchUrl = "http://open.mapquestapi.com/nominatim/v1/search?format=json&q={QUERY}&key={APIKEY}";

Expand Down Expand Up @@ -324,8 +350,13 @@ public static List<JSONObject> osmNominatimSearch(URLCache urlCache, String osmS
// ArcGIS
// API: http://resources.arcgis.com/en/help/rest/apiref/index.html?find.html
// URL example: http://www.gbrmpa.gov.au/spatial_services/gbrmpaBounds/MapServer/find?f=json&contains=true&returnGeometry=true&layers=6%2C0&searchFields=LOC_NAME_L%2CNAME&searchText={QUERY}
public static List<JSONObject> arcGISSearch(URLCache urlCache, String referer, String arcGISSearchUrl, String encodedQuery, String mapBounds)
throws JSONException, IOException, TransformException, FactoryException, URISyntaxException, SQLException, RevivableThreadInterruptedException, ClassNotFoundException {
public static List<JSONObject> arcGISSearch(
URLCache urlCache,
String referer,
String arcGISSearchUrl,
String encodedQuery,
LocationSearch.LocationSearchBoundingBox mapBounds
) throws JSONException, IOException, TransformException, FactoryException, URISyntaxException, SQLException, RevivableThreadInterruptedException, ClassNotFoundException {

if (Utils.isBlank(arcGISSearchUrl)) {
return null;
Expand Down Expand Up @@ -474,4 +505,49 @@ public static JSONObject _createSearchResult(String title, String id, double[] c

return result;
}

public static class LocationSearchBoundingBox {
private final float north;
private final float east;
private final float south;
private final float west;

public LocationSearchBoundingBox(String northStr, String eastStr, String southStr, String westStr) {
this(
Float.parseFloat(northStr),
Float.parseFloat(eastStr),
Float.parseFloat(southStr),
Float.parseFloat(westStr)
);
}

public LocationSearchBoundingBox(float north, float east, float south, float west) {
this.north = north;
this.east = east;
this.south = south;
this.west = west;
}

public float getNorth() {
return this.north;
}

public float getEast() {
return this.east;
}

public float getSouth() {
return this.south;
}

public float getWest() {
return this.west;
}

@Override
public String toString() {
return String.format("[N:%.3f, E:%.3f, S:%.3f, W:%.3f]",
this.north, this.east, this.south, this.west);
}
}
}
12 changes: 6 additions & 6 deletions src/main/java/au/gov/aims/atlasmapperserver/cache/URLCache.java
Original file line number Diff line number Diff line change
Expand Up @@ -355,12 +355,6 @@ private void requestHttpDocument(CacheEntry cacheEntry) throws URISyntaxExceptio
cacheEntry.setDocumentFile(tmpFile);
}
} finally {
if (httpGet != null) {
// Cancel the connection, if it's still alive
httpGet.abort();
// Close connections
httpGet.reset();
}
if (in != null) {
try { in.close(); } catch (Exception e) {
LOGGER.log(Level.WARNING, String.format("Error occur while closing the URL %s: %s", url, Utils.getExceptionMessage(e)), e);
Expand All @@ -376,6 +370,12 @@ private void requestHttpDocument(CacheEntry cacheEntry) throws URISyntaxExceptio
LOGGER.log(Level.WARNING, String.format("Error occur while closing the HttpResponse: %s", Utils.getExceptionMessage(e)), e);
}
}
if (httpGet != null) {
// Cancel the connection, if it's still alive
httpGet.abort();
// Close connections
httpGet.reset();
}
if (httpClient != null) {
try { httpClient.close(); } catch (Exception e) {
LOGGER.log(Level.WARNING, "Error occur while closing the HttpClient: " + Utils.getExceptionMessage(e), e);
Expand Down
17 changes: 16 additions & 1 deletion src/main/webapp/public/search.jsp
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@
<%@page import="org.json.JSONArray"%>
<%@page import="org.json.JSONObject"%>
<%@page import="au.gov.aims.atlasmapperserver.cache.URLCache" %>
<%@page import="au.gov.aims.atlasmapperserver.LocationSearch" %>
<%
// Needed by StripTagProxy:
// http://docs.sencha.com/extjs/3.4.0/#!/api/Ext.data.ScriptTagProxy
Expand All @@ -66,8 +67,22 @@
// The query string, as entered by the user in the search field.
String query = request.getParameter("query");
// Map bounds, to help the server to order the results.
String bounds = request.getParameter("bounds");
LocationSearch.LocationSearchBoundingBox bounds = null;
String northStr = request.getParameter("north");
String eastStr = request.getParameter("east");
String southStr = request.getParameter("south");
String westStr = request.getParameter("west");
if (
northStr != null && !northStr.isEmpty() &&
eastStr != null && !eastStr.isEmpty() &&
southStr != null && !southStr.isEmpty() &&
westStr != null && !westStr.isEmpty()
) {
bounds = new LocationSearch.LocationSearchBoundingBox(
northStr, eastStr, southStr, westStr);
}
// Start from result (default: 0 => Start from the first result).
int offset = (request.getParameter("offset") != null ? Integer.parseInt(request.getParameter("offset")) :
Expand Down

0 comments on commit e4c0e06

Please sign in to comment.