diff --git a/README.md b/README.md index c906250..3eef63d 100644 --- a/README.md +++ b/README.md @@ -351,6 +351,12 @@ self-hosting this service. ## Release Notes +### 2.4.7.0 + +* Added `?country=` parameter for mapcode encoding, to limit the result list to a specific country, given +as a 2- or 3-character ISO 3166 country code. (In contrast with `?territory=`, which is not always a country, +but can be a state as well.) + ### 2.4.6.0 - 2.4.6.2 * Updated to new Mapcode library. diff --git a/deployment/pom.xml b/deployment/pom.xml index 61ebefc..4db8d3c 100644 --- a/deployment/pom.xml +++ b/deployment/pom.xml @@ -23,7 +23,7 @@ com.mapcode mapcode-rest-service - 2.4.6.2 + 2.4.7.0 deployment diff --git a/pom.xml b/pom.xml index 6e55ff3..196e9d7 100644 --- a/pom.xml +++ b/pom.xml @@ -24,7 +24,7 @@ mapcode-rest-service pom - 2.4.6.2 + 2.4.7.0 Mapcode REST API Web Service @@ -104,7 +104,7 @@ 3.0.2 4.12 1.2.17 - 2.4.6 + 2.4.7 2.18.3 3.5.1.Final 2.12.6 diff --git a/resources/pom.xml b/resources/pom.xml index bc69a56..33e1423 100644 --- a/resources/pom.xml +++ b/resources/pom.xml @@ -23,7 +23,7 @@ com.mapcode mapcode-rest-service - 2.4.6.2 + 2.4.7.0 resources diff --git a/service/pom.xml b/service/pom.xml index 3ac3777..13670fb 100644 --- a/service/pom.xml +++ b/service/pom.xml @@ -23,7 +23,7 @@ com.mapcode mapcode-rest-service - 2.4.6.2 + 2.4.7.0 service diff --git a/service/src/main/java/com/mapcode/services/MapcodeResource.java b/service/src/main/java/com/mapcode/services/MapcodeResource.java index fd8a28e..4d83b2b 100644 --- a/service/src/main/java/com/mapcode/services/MapcodeResource.java +++ b/service/src/main/java/com/mapcode/services/MapcodeResource.java @@ -54,6 +54,7 @@ enum ParamInclude { static final String PARAM_LON_DEG = "lonDeg"; static final String PARAM_PRECISION = "precision"; static final String PARAM_TERRITORY = "territory"; + static final String PARAM_COUNTRY = "country"; static final String PARAM_ALPHABET = "alphabet"; static final String PARAM_CONTEXT = "context"; static final String PARAM_TYPE = "type"; @@ -83,6 +84,9 @@ void convertLatLonToMapcode( * @param paramTerritory Specifies a territory context to create a local mapcode for. This is only useful for local mapcodes. * If the mapcode cannot be created for the territory, an exception is thrown. * Range: any valid territory code, alpha or numeric. + * @param paramCountry Specifies a country context to create a local mapcode for. See also 'paramTerritory'. + * If the mapcode cannot be created for the country, an exception is thrown. + * Range: any valid country code. * @param paramAlphabet Alphabet. Range: any valid alphabet code, alpha or numeric. * @param paramInclude Specifies whether to include the offset (in meters) from the mapcode center to the specified lat/lon. * Range: {@link ParamInclude}. @@ -125,6 +129,11 @@ void convertLatLonToMapcode( "is already set by the context of the application (for example, you are only interested " + "in mapcodes for your country). If omitted, mapcodes for all territories are considered.") @QueryParam(PARAM_TERRITORY) @Nullable String paramTerritory, + @ApiParam( + value = "(optional) Limit the returned mapcodes to this country. This is useful if the country " + + "is already set by the context of the application (for example, you are only interested " + + "in mapcodes for your country). If omitted, mapcodes for all countries are considered.") + @QueryParam(PARAM_COUNTRY) @Nullable String paramCountry, @ApiParam(hidden = true) @QueryParam(PARAM_CONTEXT) @Nullable String paramContextMustBeNull, @ApiParam( @@ -156,6 +165,9 @@ void convertLatLonToMapcode( * @param paramTerritory Specifies a territory context to create a local mapcode for. This is only useful for local mapcodes. * If the mapcode cannot be created for the territory, an exception is thrown. * Range: any valid territory code, alpha or numeric. + * @param paramCountry Specifies a country context to create a local mapcode for. See also 'paramTerritory'. + * If the mapcode cannot be created for the country, an exception is thrown. + * Range: any valid country code. * @param paramAlphabet Alphabet. Range: any valid alphabet code, alpha or numeric. * @param paramInclude Specifies whether to include additional info in the result, such as the offset (in meters) * from the mapcode center to the specified lat/lon, or the encompassing rectangle. @@ -176,6 +188,7 @@ void convertLatLonToMapcode( @PathParam(PARAM_TYPE) @Nullable String paramType, @QueryParam(PARAM_PRECISION) @DefaultValue("0") int paramPrecision, @QueryParam(PARAM_TERRITORY) @Nullable String paramTerritory, + @QueryParam(PARAM_COUNTRY) @Nullable String paramCountry, @QueryParam(PARAM_CONTEXT) @Nullable String paramContextMustBeNull, @QueryParam(PARAM_ALPHABET) @Nullable String paramAlphabet, @QueryParam(PARAM_INCLUDE) @DefaultValue("") @Nonnull String paramInclude, diff --git a/service/src/main/java/com/mapcode/services/OnlyJsonResource.java b/service/src/main/java/com/mapcode/services/OnlyJsonResource.java index 63fdf7e..0a67819 100644 --- a/service/src/main/java/com/mapcode/services/OnlyJsonResource.java +++ b/service/src/main/java/com/mapcode/services/OnlyJsonResource.java @@ -57,6 +57,7 @@ void convertLatLonToMapcodeJson( @PathParam(PARAM_LON_DEG) double paramLonDeg, @QueryParam(PARAM_PRECISION) @DefaultValue("0") int paramPrecision, @QueryParam(PARAM_TERRITORY) @Nullable String paramTerritory, + @QueryParam(PARAM_COUNTRY) @Nullable String paramCountry, @QueryParam(PARAM_CONTEXT) @Nullable String paramContextMustBeNull, @QueryParam(PARAM_ALPHABET) @Nullable String paramAlphabet, @QueryParam(PARAM_INCLUDE) @DefaultValue("") @Nonnull String paramInclude, @@ -73,6 +74,7 @@ void convertLatLonToMapcodeJson( @PathParam(PARAM_TYPE) @Nullable String paramType, @QueryParam(PARAM_PRECISION) @DefaultValue("0") int paramPrecision, @QueryParam(PARAM_TERRITORY) @Nullable String paramTerritory, + @QueryParam(PARAM_COUNTRY) @Nullable String paramCountry, @QueryParam(PARAM_CONTEXT) @Nullable String paramContextMustBeNull, @QueryParam(PARAM_ALPHABET) @Nullable String paramAlphabet, @QueryParam(PARAM_INCLUDE) @DefaultValue("") @Nonnull String paramInclude, diff --git a/service/src/main/java/com/mapcode/services/OnlyXmlResource.java b/service/src/main/java/com/mapcode/services/OnlyXmlResource.java index e1207b2..ba98a6f 100644 --- a/service/src/main/java/com/mapcode/services/OnlyXmlResource.java +++ b/service/src/main/java/com/mapcode/services/OnlyXmlResource.java @@ -57,6 +57,7 @@ void convertLatLonToMapcodeXml( @PathParam(PARAM_LON_DEG) double paramLonDeg, @QueryParam(PARAM_PRECISION) @DefaultValue("0") int paramPrecision, @QueryParam(PARAM_TERRITORY) @Nullable String paramTerritory, + @QueryParam(PARAM_COUNTRY) @Nullable String paramCountry, @QueryParam(PARAM_CONTEXT) @Nullable String paramContextMustBeNull, @QueryParam(PARAM_ALPHABET) @Nullable String paramAlphabet, @QueryParam(PARAM_INCLUDE) @DefaultValue("") @Nonnull String paramInclude, @@ -73,6 +74,7 @@ void convertLatLonToMapcodeXml( @PathParam(PARAM_TYPE) @Nullable String paramType, @QueryParam(PARAM_PRECISION) @DefaultValue("0") int paramPrecision, @QueryParam(PARAM_TERRITORY) @Nullable String paramTerritory, + @QueryParam(PARAM_COUNTRY) @Nullable String paramCountry, @QueryParam(PARAM_CONTEXT) @Nullable String paramContextMustBeNull, @QueryParam(PARAM_ALPHABET) @Nullable String paramAlphabet, @QueryParam(PARAM_INCLUDE) @DefaultValue("") @Nonnull String paramInclude, diff --git a/service/src/main/java/com/mapcode/services/implementation/MapcodeResourceImpl.java b/service/src/main/java/com/mapcode/services/implementation/MapcodeResourceImpl.java index 413e2a0..b50aedc 100644 --- a/service/src/main/java/com/mapcode/services/implementation/MapcodeResourceImpl.java +++ b/service/src/main/java/com/mapcode/services/implementation/MapcodeResourceImpl.java @@ -25,10 +25,7 @@ import com.mapcode.services.dto.*; import com.mapcode.services.metrics.SystemMetricsCollector; import com.tomtom.speedtools.apivalidation.ApiDTO; -import com.tomtom.speedtools.apivalidation.exceptions.ApiForbiddenException; -import com.tomtom.speedtools.apivalidation.exceptions.ApiIntegerOutOfRangeException; -import com.tomtom.speedtools.apivalidation.exceptions.ApiInvalidFormatException; -import com.tomtom.speedtools.apivalidation.exceptions.ApiNotFoundException; +import com.tomtom.speedtools.apivalidation.exceptions.*; import com.tomtom.speedtools.geometry.Geo; import com.tomtom.speedtools.geometry.GeoPoint; import com.tomtom.speedtools.objects.Tuple; @@ -65,6 +62,9 @@ public class MapcodeResourceImpl implements MapcodeResource { private static final String API_ERROR_VALID_TERRITORY_CODES = Joiner.on('|').join(Arrays.stream(Territory.values()). collect(Collectors.toList())); + private static final String API_ERROR_VALID_COUNTRY_CODES = Joiner.on('|').join(Territory.allCountryISO2Codes()) + + '|' + Joiner.on('|').join(Territory.allCountryISO3Codes()); + private static final String API_ERROR_VALID_ALPHABET_CODES = Joiner.on('|').join(Arrays.stream(Alphabet.values()). collect(Collectors.toList())); @@ -109,16 +109,18 @@ public void convertLatLonToMapcode( final double paramLonDeg, final int paramPrecision, @Nullable final String paramTerritory, + @Nullable final String paramCountry, @Nullable final String paramContextMustBeNull, @Nullable final String paramAlphabet, @Nonnull final String paramInclude, @Nonnull final String paramClient, @Nonnull final String paramAllowLog, @Nonnull final AsyncResponse response) throws ApiInvalidFormatException { - convertLatLonToMapcode(paramLatDeg, paramLonDeg, null, paramPrecision, paramTerritory, paramContextMustBeNull, - paramAlphabet, paramInclude, paramClient, paramAllowLog, response); + convertLatLonToMapcode(paramLatDeg, paramLonDeg, null, paramPrecision, paramTerritory, paramCountry, + paramContextMustBeNull, paramAlphabet, paramInclude, paramClient, paramAllowLog, response); } + @SuppressWarnings("NestedTryStatement") @Override public void convertLatLonToMapcode( final double paramLatDeg, @@ -126,6 +128,7 @@ public void convertLatLonToMapcode( @Nullable final String paramType, final int paramPrecision, @Nullable final String paramTerritory, + @Nullable final String paramCountry, @Nullable final String paramContextMustBeNull, @Nullable final String paramAlphabet, @Nonnull final String paramInclude, @@ -164,13 +167,34 @@ public void convertLatLonToMapcode( ", " + ApiConstants.API_PRECISION_MAX + ']'); } + // Check if either paramTerritory or paramCountry is set (or neither). + if ((paramTerritory != null) && (paramCountry != null)) { + throw new ApiConflictException("Cannot specify both " + PARAM_TERRITORY + " and " + PARAM_COUNTRY); + } + // Get the territory. - @Nullable final Territory territory; - try { - territory = (paramTerritory != null) ? - resolveTerritory(StringEscapeUtils.unescapeHtml4(paramTerritory), null) : null; - } catch (final IllegalArgumentException ignored) { - throw new ApiInvalidFormatException(PARAM_TERRITORY, paramTerritory, API_ERROR_VALID_TERRITORY_CODES); + @Nullable Territory territory = null; + @Nullable String country = null; + if (paramTerritory != null) { + try { + territory = resolveTerritory(StringEscapeUtils.unescapeHtml4(paramTerritory), null); + } catch (final IllegalArgumentException ignored) { + throw new ApiInvalidFormatException(PARAM_TERRITORY, paramTerritory, API_ERROR_VALID_TERRITORY_CODES); + } + } else if (paramCountry != null) { + try { + final String countryUnescaped = StringEscapeUtils.unescapeHtml4(paramCountry); + //noinspection NestedTryStatement + try { + Territory.fromCountryISO2(countryUnescaped); + country = countryUnescaped; + } catch (final IllegalArgumentException ignored) { + Territory.fromCountryISO3(countryUnescaped); + country = countryUnescaped; + } + } catch (final IllegalArgumentException ignored) { + throw new ApiInvalidFormatException(PARAM_COUNTRY, paramCountry, API_ERROR_VALID_COUNTRY_CODES); + } } // Get the alphabet. @@ -242,7 +266,11 @@ public void convertLatLonToMapcode( // Get all mapcodes. final List mapcodes; - mapcodes = MapcodeCodec.encode(latDeg, lonDeg, territory); + if (country != null) { + mapcodes = MapcodeCodec.encodeRestrictToCountryISO(latDeg, lonDeg, country); + } else { + mapcodes = MapcodeCodec.encode(latDeg, lonDeg, territory); + } mapcodes.forEach(mapcode -> { try { final Rectangle rectangle = MapcodeCodec.decodeToRectangle(mapcode.getCode(), mapcode.getTerritory()); @@ -261,10 +289,22 @@ public void convertLatLonToMapcode( // Get the shortest local mapcode. Mapcode mapcodeLocal = null; - if (territory != null) { + if (country != null) { // A territory was provided, so simply use first. - mapcodeLocal = MapcodeCodec.encodeToShortest(latDeg, lonDeg, territory); + try { + mapcodeLocal = MapcodeCodec.encodeToShortest(latDeg, lonDeg, Territory.fromCountryISO(country)); + } catch (final UnknownMapcodeException ignored) { + mapcodeLocal = null; + } + } else if (territory != null) { + + // A territory was provided, so simply use first. + try { + mapcodeLocal = MapcodeCodec.encodeToShortest(latDeg, lonDeg, territory); + } catch (final UnknownMapcodeException ignored) { + mapcodeLocal = null; + } } else { // Get the shortest code. diff --git a/service/src/main/java/com/mapcode/services/implementation/OnlyJsonResourceImpl.java b/service/src/main/java/com/mapcode/services/implementation/OnlyJsonResourceImpl.java index d96f294..f841672 100644 --- a/service/src/main/java/com/mapcode/services/implementation/OnlyJsonResourceImpl.java +++ b/service/src/main/java/com/mapcode/services/implementation/OnlyJsonResourceImpl.java @@ -64,6 +64,7 @@ public void convertLatLonToMapcodeJson( final double paramLonDeg, final int paramPrecision, @Nullable final String paramTerritory, + @Nullable final String paramCountry, @Nullable final String paramContextMustBeNull, @Nullable final String paramAlphabet, @Nonnull final String paramInclude, @@ -71,8 +72,8 @@ public void convertLatLonToMapcodeJson( @Nonnull final String paramAllowLog, @Suspended @Nonnull final AsyncResponse response) throws ApiInvalidFormatException { - mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramPrecision, paramTerritory, paramContextMustBeNull, - paramAlphabet, paramInclude, paramClient, paramAllowLog, response); + mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramPrecision, paramTerritory, paramCountry, + paramContextMustBeNull, paramAlphabet, paramInclude, paramClient, paramAllowLog, response); } @Override @@ -82,6 +83,7 @@ public void convertLatLonToMapcodeJson( @Nullable final String paramType, final int paramPrecision, @Nullable final String paramTerritory, + @Nullable final String paramCountry, @Nullable final String paramContextMustBeNull, @Nullable final String paramAlphabet, @Nonnull final String paramInclude, @@ -89,8 +91,8 @@ public void convertLatLonToMapcodeJson( @Nonnull final String paramDebug, @Suspended @Nonnull final AsyncResponse response) throws ApiInvalidFormatException { - mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramType, paramPrecision, paramTerritory, paramContextMustBeNull, - paramAlphabet, paramInclude, paramClient, paramDebug, response); + mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramType, paramPrecision, paramTerritory, paramCountry, + paramContextMustBeNull, paramAlphabet, paramInclude, paramClient, paramDebug, response); } @Override diff --git a/service/src/main/java/com/mapcode/services/implementation/OnlyXmlResourceImpl.java b/service/src/main/java/com/mapcode/services/implementation/OnlyXmlResourceImpl.java index c5ab34b..6134870 100644 --- a/service/src/main/java/com/mapcode/services/implementation/OnlyXmlResourceImpl.java +++ b/service/src/main/java/com/mapcode/services/implementation/OnlyXmlResourceImpl.java @@ -64,6 +64,7 @@ public void convertLatLonToMapcodeXml( final double paramLonDeg, final int paramPrecision, @Nullable final String paramTerritory, + @Nullable final String paramCountry, @Nullable final String paramContextMustBeNull, @Nullable final String paramAlphabet, @Nonnull final String paramInclude, @@ -71,8 +72,8 @@ public void convertLatLonToMapcodeXml( @Nonnull final String paramAllowLog, @Suspended @Nonnull final AsyncResponse response) throws ApiInvalidFormatException { - mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramPrecision, paramTerritory, paramContextMustBeNull, - paramAlphabet, paramInclude, paramClient, paramAllowLog, response); + mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramPrecision, paramTerritory, paramCountry, + paramContextMustBeNull, paramAlphabet, paramInclude, paramClient, paramAllowLog, response); } @Override @@ -82,6 +83,7 @@ public void convertLatLonToMapcodeXml( @Nullable final String paramType, final int paramPrecision, @Nullable final String paramTerritory, + @Nullable final String paramCountry, @Nullable final String paramContextMustBeNull, @Nullable final String paramAlphabet, @Nonnull final String paramInclude, @@ -89,8 +91,8 @@ public void convertLatLonToMapcodeXml( @Nonnull final String paramDebug, @Suspended @Nonnull final AsyncResponse response) throws ApiInvalidFormatException { - mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramType, paramPrecision, paramTerritory, paramContextMustBeNull, - paramAlphabet, paramInclude, paramClient, paramDebug, response); + mapcodeResource.convertLatLonToMapcode(paramLatDeg, paramLonDeg, paramType, paramPrecision, paramTerritory, paramCountry, + paramContextMustBeNull, paramAlphabet, paramInclude, paramClient, paramDebug, response); } @Override diff --git a/service/src/main/java/com/mapcode/services/implementation/RootResourceImpl.java b/service/src/main/java/com/mapcode/services/implementation/RootResourceImpl.java index 572b479..b9f2803 100644 --- a/service/src/main/java/com/mapcode/services/implementation/RootResourceImpl.java +++ b/service/src/main/java/com/mapcode/services/implementation/RootResourceImpl.java @@ -69,11 +69,21 @@ public class RootResourceImpl implements RootResource { "GET /mapcode/status Returns 200 if the service OK.\n\n" + "GET /mapcode/codes/{lat},{lon}[/[mapcodes|local|international]]\n" + - " [?precision=[0..8] & territory={restrictToTerritory} & alphabet={alphabet} & include={offset|territory|alphabet|rectangle}]\n\n" + + " [?precision=[0..8] & territory={restrictToTerritory} & country={restrictToCountry}\n" + + " alphabet={alphabet} & include={offset|territory|alphabet|rectangle}]\n\n" + " Convert latitude/longitude to one or more mapcodes. The response always contains the 'international' mapcode and\n" + " only contains a 'local' mapcode if there are any non-international mapcode AND they are all of the same territory.\n\n" + + " The 'country' parameter always specifies a country, by a 2 or 3 character ISO-3166 code, like 'US' or 'USA'\n" + + " (for the USA), and 'NL' or 'NLD' (for the Netherlands). In a web environment, the country code is often available\n" + + " as a 2-character code. That code can be used for this parameter.\n\n" + + + " The 'territory' parameter is a 2, 3 or 5 (XX-YY) character code. These code can be countries or states within countries.\n" + + " Some 2 character state codes are the same as country codes. In that case, the territory implies the state, not the country.\n" + + " For example, the territory code 'US' is unambiguous and means USA, but 'NL' means 'IN-NL' (Nagaland, India) rather than\n" + + " the Netherlands. You cannot use the standard 2-character country codes in web applications for this parameter.\n\n" + + " Path parameters:\n" + " lat : Latitude, range [-90, 90] (automatically limited to this range).\n" + " lon : Longitude, range [-180, 180] (automatically wrapped to this range).\n\n" + @@ -89,7 +99,8 @@ public class RootResourceImpl implements RootResource { " Query parameters:\n" + " precision : Precision, range [0..8] (default=0).\n" + - " territory : Territory to restrict results to (name or alphacode).\n" + + " territory : Territory (country or state) to restrict results to (name or alphacode).\n" + + " country : Country to restrict results to (name or alphacode).\n" + " alphabet : Alphabet to return results in.\n" + " include : Multiple options may be set, separated by comma's:\n" + " offset = Include offset from mapcode center to lat/lon (in meters).\n" + @@ -230,7 +241,7 @@ public void getStatus(@Suspended @Nonnull final AsyncResponse response) { final String allowLog = "false"; final TestAsyncResponse asyncResponse1 = new TestAsyncResponse(); mapcodeResource.convertLatLonToMapcode(latDeg, lonDeg, "local", - precision, territory, null, alphabet, include, client, + precision, territory, null, null, alphabet, include, client, allowLog, asyncResponse1); waitForResponse(asyncResponse1);