Skip to content

Commit da5c8be

Browse files
committed
Improved alphabet support
1 parent 89e4026 commit da5c8be

File tree

10 files changed

+113
-103
lines changed

10 files changed

+113
-103
lines changed

src/main/java/com/mapcode/Mapcode.java

Lines changed: 17 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -86,10 +86,11 @@ public Mapcode(
8686
@Nonnull final Territory territory) throws IllegalArgumentException {
8787

8888
checkMapcodeCode("code", code);
89-
if (containsTerritory(code)) {
89+
final String ascii = convertStringToPlainAscii(code);
90+
if (containsTerritory(ascii)) {
9091
throw new IllegalArgumentException("Must not contain territory: " + code);
9192
}
92-
final String codeUppercase = code.toUpperCase();
93+
final String codeUppercase = ascii.toUpperCase();
9394
this.codePrecision2 = codeUppercase;
9495
if (codeUppercase.contains("-")) {
9596
this.codePrecision0 = codeUppercase.substring(0, codeUppercase.length() - 3);
@@ -114,12 +115,12 @@ public Mapcode(
114115
*/
115116
@Nonnull
116117
public String getCode(@Nullable final Alphabet alphabet) {
117-
return convertMapcodeToAlphabet(codePrecision0, alphabet);
118+
return convertStringToAlphabet(codePrecision0, alphabet);
118119
}
119120

120121
@Nonnull
121122
public String getCode() {
122-
return convertMapcodeToAlphabet(codePrecision0, null);
123+
return convertStringToAlphabet(codePrecision0, null);
123124
}
124125

125126
/**
@@ -146,11 +147,11 @@ public String getCode() {
146147
public String getCode(final int precision, @Nullable final Alphabet alphabet) {
147148
switch (precision) {
148149
case 0:
149-
return convertMapcodeToAlphabet(codePrecision0, alphabet);
150+
return convertStringToAlphabet(codePrecision0, alphabet);
150151
case 1:
151-
return convertMapcodeToAlphabet(codePrecision1, alphabet);
152+
return convertStringToAlphabet(codePrecision1, alphabet);
152153
case 2:
153-
return convertMapcodeToAlphabet(codePrecision2, alphabet);
154+
return convertStringToAlphabet(codePrecision2, alphabet);
154155
default:
155156
throw new IllegalArgumentException("getCodePrecision: precision must be in [0, 2]");
156157
}
@@ -308,7 +309,7 @@ public static FormatType fromPrecision(final int precision) {
308309
public static FormatType getMapcodeFormatType(@Nonnull final String mapcode) throws IllegalArgumentException {
309310

310311
// First, decode to ASCII.
311-
final String decodedMapcode = convertMapcodeToPlainAscii(mapcode.toUpperCase());
312+
final String decodedMapcode = convertStringToPlainAscii(mapcode.toUpperCase());
312313

313314
// Syntax needs to be OK.
314315
if (!PATTERN_MAPCODE.matcher(decodedMapcode).matches()) {
@@ -375,29 +376,27 @@ public static double getSafeMaxOffsetInMeters(final int precision) {
375376
}
376377

377378
/**
378-
* Convert a mapcode which potentially contains Unicode characters, to an ASCII variant.
379+
* Convert a string which potentially contains Unicode characters, to an ASCII variant.
379380
*
380-
* @param mapcode Mapcode (optionally with a territory), with optional Unicode characters.
381+
* @param string Any string.
381382
* @return ASCII, non-Unicode string.
382383
*/
383384
@Nonnull
384-
static String convertMapcodeToPlainAscii(@Nonnull final String mapcode) {
385-
// Cannot call: checkMapcodeCode() - recursive.
386-
return Decoder.decodeUTF16(mapcode.toUpperCase());
385+
static String convertStringToPlainAscii(@Nonnull final String string) {
386+
return Decoder.decodeUTF16(string.toUpperCase());
387387
}
388388

389389
/**
390-
* Convert a mapcode into the same mapcode using a different (or the same) alphabet.
390+
* Convert a string into the same string using a different (or the same) alphabet.
391391
*
392-
* @param mapcode Mapcode (optionally with a territory) to be converted.
392+
* @param string Any string.
393393
* @param alphabet Alphabet to convert to, may contain Unicode characters.
394394
* @return Converted mapcode.
395395
* @throws IllegalArgumentException Thrown if mapcode has incorrect syntax.
396396
*/
397397
@Nonnull
398-
static String convertMapcodeToAlphabet(@Nonnull final String mapcode, @Nullable final Alphabet alphabet) throws IllegalArgumentException {
399-
checkMapcodeCode("mapcode", mapcode);
400-
return (alphabet != null) ? Decoder.encodeUTF16(mapcode.toUpperCase(), alphabet.getCode()) : mapcode.toUpperCase();
398+
static String convertStringToAlphabet(@Nonnull final String string, @Nullable final Alphabet alphabet) throws IllegalArgumentException {
399+
return (alphabet != null) ? Decoder.encodeUTF16(string.toUpperCase(), alphabet.getCode()) : string.toUpperCase();
401400
}
402401

403402
/**

src/main/java/com/mapcode/MapcodeCodec.java

Lines changed: 27 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020
import org.slf4j.LoggerFactory;
2121

2222
import javax.annotation.Nonnull;
23+
import javax.annotation.Nullable;
2324
import java.util.List;
2425
import java.util.regex.Matcher;
2526

@@ -65,20 +66,10 @@ private MapcodeCodec() {
6566
* @throws IllegalArgumentException Thrown if latitude or longitude are out of range.
6667
*/
6768
@Nonnull
68-
public static List<Mapcode> encode(
69-
final double latDeg,
70-
final double lonDeg) throws IllegalArgumentException {
71-
checkRange("latDeg", latDeg, Point.LAT_DEG_MIN, Point.LAT_DEG_MAX);
72-
checkRange("lonDeg", lonDeg, Point.LON_DEG_MIN, Point.LON_DEG_MAX);
73-
74-
// Call mapcode encoder.
75-
@Nonnull final List<Mapcode> results = Encoder.encode(latDeg, lonDeg, null, false, false, true);
76-
assert results != null;
77-
assert results.size() >= 1;
78-
return results;
69+
public static List<Mapcode> encode(final double latDeg, final double lonDeg) throws IllegalArgumentException {
70+
return encode(latDeg, lonDeg, null);
7971
}
8072

81-
// Convenience method.
8273
@Nonnull
8374
public static List<Mapcode> encode(@Nonnull final Point point) throws IllegalArgumentException {
8475
checkNonnull("point", point);
@@ -97,33 +88,26 @@ public static List<Mapcode> encode(@Nonnull final Point point) throws IllegalArg
9788
*
9889
* @param latDeg Latitude, accepted range: -90..90.
9990
* @param lonDeg Longitude, accepted range: -180..180.
100-
* @param restrictToTerritory Try to encode only within this territory, see {@link Territory}. Cannot
101-
* be null.
91+
* @param restrictToTerritory Try to encode only within this territory, see {@link Territory}. May be null.
10292
* @return List of mapcode information records, see {@link Mapcode}. This list is empty if no
10393
* Mapcode can be generated for this territory matching the lat/lon.
10494
* @throws IllegalArgumentException Thrown if latitude or longitude are out of range.
10595
*/
10696
@Nonnull
107-
public static List<Mapcode> encode(
108-
final double latDeg,
109-
final double lonDeg,
110-
@Nonnull final Territory restrictToTerritory) throws IllegalArgumentException {
97+
public static List<Mapcode> encode(final double latDeg, final double lonDeg,
98+
@Nullable final Territory restrictToTerritory) throws IllegalArgumentException {
11199
checkRange("latDeg", latDeg, Point.LAT_DEG_MIN, Point.LAT_DEG_MAX);
112100
checkRange("lonDeg", lonDeg, Point.LON_DEG_MIN, Point.LON_DEG_MAX);
113-
checkNonnull("restrictToTerritory", restrictToTerritory);
114101

115102
// Call Mapcode encoder.
116-
@Nonnull final List<Mapcode> results =
117-
Encoder.encode(latDeg, lonDeg, restrictToTerritory, false, false, false);
103+
final List<Mapcode> results = Encoder.encode(latDeg, lonDeg, restrictToTerritory, false, false, (restrictToTerritory == null));
118104
assert results != null;
119105
return results;
120106
}
121107

122-
// Convenience method.
123108
@Nonnull
124-
public static List<Mapcode> encode(
125-
@Nonnull final Point point,
126-
@Nonnull final Territory restrictToTerritory) throws IllegalArgumentException {
109+
public static List<Mapcode> encode(@Nonnull final Point point,
110+
@Nullable final Territory restrictToTerritory) throws IllegalArgumentException {
127111
checkNonnull("point", point);
128112
return encode(point.getLatDeg(), point.getLonDeg(), restrictToTerritory);
129113
}
@@ -138,20 +122,14 @@ public static List<Mapcode> encode(
138122
* @throws IllegalArgumentException Thrown if latitude or longitude are out of range.
139123
*/
140124
@Nonnull
141-
public static Mapcode encodeToShortest(
142-
final double latDeg,
143-
final double lonDeg) throws IllegalArgumentException {
144-
checkRange("latDeg", latDeg, Point.LAT_DEG_MIN, Point.LAT_DEG_MAX);
145-
checkRange("lonDeg", lonDeg, Point.LON_DEG_MIN, Point.LON_DEG_MAX);
146-
147-
// Call mapcode encoder.
148-
@Nonnull final List<Mapcode> results = Encoder.encode(latDeg, lonDeg, null, false, true, true);
149-
assert results != null;
150-
assert results.size() == 1;
151-
return results.get(0);
125+
public static Mapcode encodeToShortest(final double latDeg, final double lonDeg) throws IllegalArgumentException {
126+
try {
127+
return encodeToShortest(latDeg, lonDeg, null);
128+
} catch (final UnknownMapcodeException e) {
129+
throw new IllegalStateException("Encoding should never fail for + " + latDeg + ", " + lonDeg, e);
130+
}
152131
}
153132

154-
// Convenience method.
155133
@Nonnull
156134
public static Mapcode encodeToShortest(@Nonnull final Point point) throws IllegalArgumentException {
157135
checkNonnull("point", point);
@@ -163,24 +141,20 @@ public static Mapcode encodeToShortest(@Nonnull final Point point) throws Illega
163141
*
164142
* @param latDeg Latitude, accepted range: -90..90.
165143
* @param lonDeg Longitude, accepted range: -180..180.
166-
* @param restrictToTerritory Try to encode only within this territory, see {@link Territory}. Cannot
167-
* be null.
144+
* @param restrictToTerritory Try to encode only within this territory, see {@link Territory}. May be null.
168145
* @return Shortest mapcode, see {@link Mapcode}.
169146
* @throws IllegalArgumentException Thrown if latitude or longitude are out of range.
170147
* @throws UnknownMapcodeException Thrown if no mapcode was found for the lat/lon matching the territory.
171148
*/
172149
@Nonnull
173-
public static Mapcode encodeToShortest(
174-
final double latDeg,
175-
final double lonDeg,
176-
@Nonnull final Territory restrictToTerritory) throws IllegalArgumentException, UnknownMapcodeException {
150+
public static Mapcode encodeToShortest(final double latDeg, final double lonDeg,
151+
@Nullable final Territory restrictToTerritory) throws IllegalArgumentException, UnknownMapcodeException {
177152
checkRange("latDeg", latDeg, Point.LAT_DEG_MIN, Point.LAT_DEG_MAX);
178153
checkRange("lonDeg", lonDeg, Point.LON_DEG_MIN, Point.LON_DEG_MAX);
179-
checkNonnull("restrictToTerritory", restrictToTerritory);
180154

181155
// Call mapcode encoder.
182156
@Nonnull final List<Mapcode> results =
183-
Encoder.encode(latDeg, lonDeg, restrictToTerritory, false, true, false);
157+
Encoder.encode(latDeg, lonDeg, restrictToTerritory, false, true, (restrictToTerritory == null));
184158
assert results != null;
185159
assert results.size() <= 1;
186160
if (results.isEmpty()) {
@@ -190,11 +164,9 @@ public static Mapcode encodeToShortest(
190164
return results.get(0);
191165
}
192166

193-
// Convenience method.
194167
@Nonnull
195-
public static Mapcode encodeToShortest(
196-
@Nonnull final Point point,
197-
@Nonnull final Territory restrictToTerritory) throws IllegalArgumentException, UnknownMapcodeException {
168+
public static Mapcode encodeToShortest(@Nonnull final Point point,
169+
@Nullable final Territory restrictToTerritory) throws IllegalArgumentException, UnknownMapcodeException {
198170
checkNonnull("point", point);
199171
return encodeToShortest(point.getLatDeg(), point.getLonDeg(), restrictToTerritory);
200172
}
@@ -208,9 +180,7 @@ public static Mapcode encodeToShortest(
208180
* @throws IllegalArgumentException Thrown if latitude or longitude are out of range.
209181
*/
210182
@Nonnull
211-
public static Mapcode encodeToInternational(
212-
final double latDeg,
213-
final double lonDeg) throws IllegalArgumentException {
183+
public static Mapcode encodeToInternational(final double latDeg, final double lonDeg) throws IllegalArgumentException {
214184
checkRange("latDeg", latDeg, Point.LAT_DEG_MIN, Point.LAT_DEG_MAX);
215185
checkRange("lonDeg", lonDeg, Point.LON_DEG_MIN, Point.LON_DEG_MAX);
216186

@@ -221,7 +191,6 @@ public static Mapcode encodeToInternational(
221191
return results.get(results.size() - 1);
222192
}
223193

224-
// Convenience method.
225194
@Nonnull
226195
public static Mapcode encodeToInternational(@Nonnull final Point point) throws IllegalArgumentException {
227196
checkNonnull("point", point);
@@ -263,28 +232,27 @@ public static Point decode(@Nonnull final String mapcode) throws UnknownMapcodeE
263232
* Note that if a territory-code is supplied in the string, it takes preferences over the parameter.
264233
*
265234
* @param mapcode Mapcode.
266-
* @param defaultTerritoryContext Default territory context for disambiguation purposes.
235+
* @param defaultTerritoryContext Default territory context for disambiguation purposes. May be null.
267236
* @return Point corresponding to mapcode. Latitude range: -90..90, longitude range: -180..180.
268237
* @throws UnknownMapcodeException Thrown if the mapcode has the right syntax, but cannot be decoded into a point.
269238
* @throws IllegalArgumentException Thrown if arguments are null, or if the syntax of the mapcode is incorrect.
270239
*/
271240
@Nonnull
272241
public static Point decode(
273242
@Nonnull final String mapcode,
274-
@Nonnull final Territory defaultTerritoryContext) throws UnknownMapcodeException, IllegalArgumentException {
243+
@Nullable final Territory defaultTerritoryContext) throws UnknownMapcodeException, IllegalArgumentException {
275244
checkNonnull("mapcode", mapcode);
276-
checkNonnull("territoryContext", defaultTerritoryContext);
277245

278246
// Clean up mapcode.
279-
String mapcodeClean = mapcode.trim().toUpperCase();
247+
String mapcodeClean = Mapcode.convertStringToPlainAscii(mapcode.trim().toUpperCase());
280248

281249
// Determine territory from mapcode.
282250
final Territory territory;
283251
final Matcher matcherTerritory = Mapcode.PATTERN_TERRITORY.matcher(mapcodeClean);
284252
if (!matcherTerritory.find()) {
285253

286254
// No territory code was supplied in the string, use specified territory context parameter.
287-
territory = defaultTerritoryContext;
255+
territory = (defaultTerritoryContext != null) ? defaultTerritoryContext : Territory.AAA;
288256
} else {
289257

290258
// Use the territory code from the string.
@@ -296,7 +264,7 @@ public static Point decode(
296264
}
297265

298266
// Cut off the territory part.
299-
mapcodeClean = mapcode.substring(matcherTerritory.end()).trim();
267+
mapcodeClean = mapcodeClean.substring(matcherTerritory.end()).trim();
300268
}
301269

302270
if (!Mapcode.isValidMapcodeFormat(mapcodeClean)) {

src/main/java/com/mapcode/Territory.java

Lines changed: 27 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -685,14 +685,27 @@ public static Territory fromString(
685685
}
686686

687687
/**
688-
* Return the territory name, with dashes rather than underscores.
688+
* Return the international value of the territory name, with dashes rather than underscores.
689+
* This is the same as {@link #toNameFormat(NameFormat)} for {@link NameFormat#INTERNATIONAL}.
690+
*
691+
* @param alphabet Alphabet. May be null.
692+
* @return Territory name. Underscores have been replaced with dashes.
693+
*/
694+
@Nonnull
695+
public String toString(@Nullable final Alphabet alphabet) {
696+
return toNameFormat(NameFormat.INTERNATIONAL, alphabet);
697+
}
698+
699+
/**
700+
* Return the international value of the territory name, with dashes rather than underscores.
701+
* This is the same as {@link #toNameFormat(NameFormat)} for {@link NameFormat#INTERNATIONAL}.
689702
*
690703
* @return Territory name. Underscores have been replaced with dashes.
691704
*/
692705
@Override
693706
@Nonnull
694707
public String toString() {
695-
return name().replace('_', '-');
708+
return toString(null);
696709
}
697710

698711
/**
@@ -707,23 +720,30 @@ public enum NameFormat {
707720
/**
708721
* Return the territory name, given a specific territory name format.
709722
*
710-
* @param format Format to be used.
723+
* @param format Format to be used.
724+
* @param alphabet Alphabet. May be null.
711725
* @return Mapcode
712726
*/
713727
@Nonnull
714-
public String toNameFormat(@Nonnull final NameFormat format) {
728+
public String toNameFormat(@Nonnull final NameFormat format, @Nullable final Alphabet alphabet) {
715729
checkNonnull("format", format);
730+
String result = name().replace('_', '-');
716731
if (format != NameFormat.INTERNATIONAL) {
717732
final int index = name().indexOf('_');
718733
if (index != -1) {
719734
assert name().length() > (index + 1);
720735
final String shortName = name().substring(index + 1);
721736
if ((format == NameFormat.MINIMAL) || (nameMap.get(shortName).size() == 1)) {
722-
return shortName;
737+
result = shortName;
723738
}
724739
}
725740
}
726-
return toString();
741+
return (alphabet != null) ? Mapcode.convertStringToAlphabet(result, alphabet) : result;
742+
}
743+
744+
@Nonnull
745+
public String toNameFormat(@Nonnull final NameFormat format) {
746+
return toNameFormat(format, null);
727747
}
728748

729749
/**
@@ -842,7 +862,7 @@ private Territory(
842862
private static Territory createFromString(
843863
@Nonnull final String numericOrAlpha,
844864
@Nullable final ParentTerritory parentTerritory) throws UnknownTerritoryException {
845-
final String trimmed = numericOrAlpha.trim().replace('_', '-').toUpperCase();
865+
final String trimmed = Mapcode.convertStringToPlainAscii(numericOrAlpha.trim().replace('_', '-').toUpperCase());
846866

847867
// First, try as numeric code.
848868
try {

src/site/apt/ReleaseNotes.apt.vm

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,10 +23,12 @@ Release Notes (Version ${project.version})
2323
* Added convencience methods for <<<MapcodeCodec>>> to accept <<<Point>>> for all encode functions
2424
as well (not just <<<latDeg>>>, <<<lonDeg>>>).
2525

26-
* Added alphabet support to convert mapcodes between <<<Alphabet>>>s.
26+
* Added alphabet support to convert mapcodes (both codes and territories) between <<<Alphabet>>>s.
2727

2828
* Exceptions have been corrected and documented in code.
2929

30+
* Allowed nullable values in <<<MapcodeCodec>>> encode and decode methods to assume reasonable defaults.
31+
3032
[]
3133

3234

0 commit comments

Comments
 (0)