Skip to content

Commit 0fad1c5

Browse files
committed
Merge pull request #13 from mapcode-foundation/dev
1.50.2
2 parents a750da8 + aa9db7d commit 0fad1c5

File tree

8 files changed

+168
-158
lines changed

8 files changed

+168
-158
lines changed

pom.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@
88
<artifactId>mapcode</artifactId>
99

1010
<packaging>jar</packaging>
11-
<version>1.50.1</version>
11+
<version>1.50.2</version>
1212

1313
<name>Mapcode Java Library</name>
1414
<description>

src/main/java/com/mapcode/DataAccess.java

Lines changed: 36 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616

1717
package com.mapcode;
1818

19+
import org.slf4j.Logger;
20+
import org.slf4j.LoggerFactory;
21+
1922
import java.io.ByteArrayOutputStream;
2023
import java.io.IOException;
2124
import java.io.InputStream;
@@ -28,61 +31,61 @@
2831
* This class contains the module that reads the Mapcode areas into memory and processes them.
2932
*/
3033
class DataAccess {
34+
private static final Logger LOG = LoggerFactory.getLogger(DataAccess.class);
3135

32-
private static final byte[] FILE_DATA;
36+
private static final int[] FILE_DATA;
3337
private static final String FILE_NAME = "/com/mapcode/mminfo.dat";
3438

3539
// Read data only once in static initializer.
3640
static {
37-
final InputStream inputStream = DataAccess.class.getResourceAsStream(FILE_NAME);
38-
try {
39-
final ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
40-
for (int readBytes = inputStream.read(); readBytes >= 0; readBytes = inputStream.read()) {
41-
outputStream.write(readBytes);
42-
}
41+
LOG.info("DataAccess: reading regions from file: {}", FILE_NAME);
42+
final int bufferSize = 100000;
43+
final byte[] readBuffer = new byte[bufferSize];
44+
int total = 0;
45+
try (final InputStream inputStream = DataAccess.class.getResourceAsStream(FILE_NAME)) {
46+
try (final ByteArrayOutputStream outputStream = new ByteArrayOutputStream()) {
47+
int nrBytes = inputStream.read(readBuffer);
48+
while (nrBytes >= 0) {
49+
total += nrBytes;
50+
outputStream.write(readBuffer, 0, nrBytes);
51+
nrBytes = inputStream.read(readBuffer);
52+
}
53+
54+
// Copy stream as unsigned bytes (ints).
55+
final byte[] bytes = outputStream.toByteArray();
56+
assert total == bytes.length;
57+
FILE_DATA = new int[total];
58+
for (int i = 0; i < total; ++i) {
59+
FILE_DATA[i] = (bytes[i] < 0) ? (bytes[i] + 256) : bytes[i];
4360

44-
FILE_DATA = outputStream.toByteArray();
45-
inputStream.close();
46-
outputStream.close();
47-
} catch (final IOException e) {
48-
throw new ExceptionInInitializerError("Cannot initialize static data structure from: " + FILE_NAME +
49-
", exception=" + e);
50-
} finally {
51-
try {
52-
if (inputStream != null) {
53-
inputStream.close();
5461
}
55-
} catch (final IOException ignored) {
56-
// Ignore.
5762
}
63+
} catch (final IOException e) {
64+
throw new ExceptionInInitializerError("Cannot initialize static data structure from: " +
65+
FILE_NAME + ", exception=" + e);
5866
}
67+
LOG.info("DataAccess: regions initialized, read {} bytes", total);
5968
}
6069

6170
private DataAccess() {
6271
// Empty.
6372
}
6473

65-
private static int asUnsignedByte(final int i) {
66-
int u = FILE_DATA[i];
67-
if (u < 0) {
68-
u += 256;
69-
}
70-
return u;
71-
}
72-
7374
static int dataFlags(final int i) {
74-
return asUnsignedByte((i * 20) + 16) + (asUnsignedByte((i * 20) + 17) * 256);
75+
return FILE_DATA[(i * 20) + 16] +
76+
(FILE_DATA[(i * 20) + 17] * 256);
7577
}
7678

7779
static int asLong(final int i) {
78-
return asUnsignedByte(i) +
79-
(asUnsignedByte(i + 1) << 8) +
80-
(asUnsignedByte(i + 2) << 16) +
81-
(asUnsignedByte(i + 3) << 24);
80+
return FILE_DATA[i] +
81+
(FILE_DATA[i + 1] << 8) +
82+
(FILE_DATA[i + 2] << 16) +
83+
(FILE_DATA[i + 3] << 24);
8284
}
8385

8486
static int smartDiv(final int i) {
85-
return asUnsignedByte((i * 20) + 18) + (asUnsignedByte((i * 20) + 19) * 256);
87+
return FILE_DATA[(i * 20) + 18] +
88+
(FILE_DATA[(i * 20) + 19] * 256);
8689
}
8790

8891
private final static int[] DATA_START = {

src/main/java/com/mapcode/Decoder.java

Lines changed: 65 additions & 72 deletions
Large diffs are not rendered by default.

src/main/java/com/mapcode/Encoder.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ static List<Mapcode> encode(
5656
private final static char[] ENCODE_CHARS = {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'B', 'C', 'D', 'F',
5757
'G', 'H', 'J', 'K', 'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'V', 'W', 'X', 'Y', 'Z', 'A', 'E', 'U'};
5858

59+
@SuppressWarnings("ConstantConditions")
5960
@Nonnull
6061
private static List<Mapcode> encode(final double argLatDeg, final double argLonDeg,
6162
@Nullable final Territory territory, final boolean isRecursive, final boolean limitToOneResult, final boolean allowWorld,

src/main/java/com/mapcode/SubArea.java

Lines changed: 38 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -34,79 +34,81 @@
3434
class SubArea {
3535
private static final Logger LOG = LoggerFactory.getLogger(SubArea.class);
3636

37-
private static final ArrayList<SubArea> subAreas = new ArrayList<>();
38-
private static final TreeMap<Integer, ArrayList<SubArea>> lonMap = new TreeMap<>();
39-
private static final TreeMap<Integer, ArrayList<SubArea>> latMap = new TreeMap<>();
37+
private static final int SUB_AREAS_INITIAL_CAPACITY = 16250;
4038

41-
private static final Range<Integer> latBoundingRange =
42-
new Range<>(Point.LAT_MICRODEG_MIN, Point.LAT_MICRODEG_MAX);
43-
private static final Range<Integer> lonBoundingRange =
44-
new Range<>(Point.LON_MICRODEG_MIN, Point.LON_MICRODEG_MAX);
39+
private static final List<SubArea> SUB_AREAS = new ArrayList<>(SUB_AREAS_INITIAL_CAPACITY);
40+
private static final TreeMap<Integer, ArrayList<SubArea>> LON_MAP = new TreeMap<>();
41+
private static final TreeMap<Integer, ArrayList<SubArea>> LAT_MAP = new TreeMap<>();
42+
43+
private static final Range<Integer> LAT_BOUNDING_RANGE = new Range<>(Point.LAT_MICRODEG_MIN, Point.LAT_MICRODEG_MAX);
44+
private static final Range<Integer> LON_BOUNDING_RANGE = new Range<>(Point.LON_MICRODEG_MIN, Point.LON_MICRODEG_MAX);
4545

4646
static {
47+
LOG.info("SubArea: Initialize sub-areas for {} territories", Territory.values().length);
4748
for (final Territory territory : Territory.values()) {
4849
final int territoryCode = territory.getCode();
4950
final int first = DataAccess.dataFirstRecord(territoryCode);
5051
final int last = DataAccess.dataLastRecord(territoryCode);
5152

5253
// Add a number sub areas.
53-
for (int i = subAreas.size(); i <= last; i++) {
54-
subAreas.add(null);
54+
for (int i = SUB_AREAS.size(); i <= last; i++) {
55+
SUB_AREAS.add(null);
5556
}
5657
for (int i = last; i >= first; i--) {
57-
final SubArea newSubArea = new SubArea(i, territory, subAreas.get(last));
58-
subAreas.set(i, newSubArea);
58+
final SubArea newSubArea = new SubArea(i, territory, SUB_AREAS.get(last));
59+
SUB_AREAS.set(i, newSubArea);
5960

6061
if ((newSubArea.boundedLatRange == null) || (newSubArea.boundedLonRange == null)) {
6162
continue;
6263
}
6364

6465
for (final Range<Integer> longitudeRange : newSubArea.boundedLonRange) {
65-
if (!lonMap.containsKey(longitudeRange.getMin())) {
66-
lonMap.put(longitudeRange.getMin(), new ArrayList<SubArea>());
66+
if (!LON_MAP.containsKey(longitudeRange.getMin())) {
67+
LON_MAP.put(longitudeRange.getMin(), new ArrayList<SubArea>());
6768
}
68-
if (!lonMap.containsKey(longitudeRange.getMax())) {
69-
lonMap.put(longitudeRange.getMax(), new ArrayList<SubArea>());
69+
if (!LON_MAP.containsKey(longitudeRange.getMax())) {
70+
LON_MAP.put(longitudeRange.getMax(), new ArrayList<SubArea>());
7071
}
7172
}
7273

7374
for (final Range<Integer> latitudeRange : newSubArea.boundedLatRange) {
74-
if (!latMap.containsKey(latitudeRange.getMin())) {
75-
latMap.put(latitudeRange.getMin(), new ArrayList<SubArea>());
75+
if (!LAT_MAP.containsKey(latitudeRange.getMin())) {
76+
LAT_MAP.put(latitudeRange.getMin(), new ArrayList<SubArea>());
7677
}
77-
if (!latMap.containsKey(latitudeRange.getMax())) {
78-
latMap.put(latitudeRange.getMax(), new ArrayList<SubArea>());
78+
if (!LAT_MAP.containsKey(latitudeRange.getMax())) {
79+
LAT_MAP.put(latitudeRange.getMax(), new ArrayList<SubArea>());
7980
}
8081
}
8182
}
8283
}
83-
for (final SubArea subArea : subAreas) {
84+
LOG.info("SubArea: Created {} sub-areas", SUB_AREAS.size());
85+
for (final SubArea subArea : SUB_AREAS) {
8486
if ((subArea.boundedLatRange == null) || (subArea.boundedLonRange == null)) {
8587
continue;
8688
}
8789
SortedMap<Integer, ArrayList<SubArea>> subMap;
8890

8991
for (final Range<Integer> longitudeRange : subArea.boundedLonRange) {
90-
subMap = lonMap.subMap(longitudeRange.getMin(), longitudeRange.getMax() + 1);
92+
subMap = LON_MAP.subMap(longitudeRange.getMin(), longitudeRange.getMax() + 1);
9193
for (final ArrayList<SubArea> areaList : subMap.values()) {
9294
areaList.add(subArea);
9395
}
9496
}
9597

9698
for (final Range<Integer> latitudeRange : subArea.boundedLatRange) {
97-
subMap = latMap.subMap(latitudeRange.getMin(), latitudeRange.getMax() + 1);
99+
subMap = LAT_MAP.subMap(latitudeRange.getMin(), latitudeRange.getMax() + 1);
98100
for (final ArrayList<SubArea> areaList : subMap.values()) {
99101
areaList.add(subArea);
100102
}
101103
}
102104
}
103-
LOG.debug("SubArea (init): lat=[{}, {}], lon=[{}, {}]",
104-
Point.microDegToDeg(latMap.firstKey()), Point.microDegToDeg(latMap.lastKey()),
105-
Point.microDegToDeg(lonMap.firstKey()), Point.microDegToDeg(lonMap.lastKey()));
105+
LOG.info("SubArea: sub-areas initialized: aslat=[{}, {}], lon=[{}, {}]",
106+
Point.microDegToDeg(LAT_MAP.firstKey()), Point.microDegToDeg(LAT_MAP.lastKey()),
107+
Point.microDegToDeg(LON_MAP.firstKey()), Point.microDegToDeg(LON_MAP.lastKey()));
106108
}
107109

108110
static SubArea getArea(final int i) {
109-
return subAreas.get(i);
111+
return SUB_AREAS.get(i);
110112
}
111113

112114

@@ -115,19 +117,19 @@ static SubArea getArea(final int i) {
115117
static List<SubArea> getAreasForPoint(@Nonnull final Point point) {
116118
final ArrayList<ArrayList<SubArea>> areaLists = new ArrayList<>();
117119
ArrayList<SubArea> list;
118-
list = latMap.get(point.getLatMicroDeg());
120+
list = LAT_MAP.get(point.getLatMicroDeg());
119121

120122
if (list != null) {
121123
areaLists.add(list);
122124
} else {
123-
Entry<Integer, ArrayList<SubArea>> entry = latMap.lowerEntry(point.getLatMicroDeg());
125+
Entry<Integer, ArrayList<SubArea>> entry = LAT_MAP.lowerEntry(point.getLatMicroDeg());
124126
if (entry == null) {
125127
return Collections.EMPTY_LIST;
126128
}
127129
list = entry.getValue();
128130
assert list != null;
129131
areaLists.add(list);
130-
entry = latMap.higherEntry(point.getLatMicroDeg());
132+
entry = LAT_MAP.higherEntry(point.getLatMicroDeg());
131133
if (entry == null) {
132134
return Collections.EMPTY_LIST;
133135
}
@@ -136,18 +138,18 @@ static List<SubArea> getAreasForPoint(@Nonnull final Point point) {
136138
areaLists.add(list);
137139
}
138140

139-
list = lonMap.get(point.getLonMicroDeg());
141+
list = LON_MAP.get(point.getLonMicroDeg());
140142
if (list != null) {
141143
areaLists.add(list);
142144
} else {
143-
Entry<Integer, ArrayList<SubArea>> entry = lonMap.lowerEntry(point.getLonMicroDeg());
145+
Entry<Integer, ArrayList<SubArea>> entry = LON_MAP.lowerEntry(point.getLonMicroDeg());
144146
if (entry == null) {
145147
return Collections.EMPTY_LIST;
146148
}
147149
list = entry.getValue();
148150
assert list != null;
149151
areaLists.add(list);
150-
entry = lonMap.higherEntry(point.getLonMicroDeg());
152+
entry = LON_MAP.higherEntry(point.getLonMicroDeg());
151153
if (entry == null) {
152154
return Collections.EMPTY_LIST;
153155
}
@@ -161,14 +163,13 @@ static List<SubArea> getAreasForPoint(@Nonnull final Point point) {
161163

162164
mainLoop:
163165
for (final SubArea subArea : list) {
164-
for (int i = 1; i < areaLists.size(); i++) {
165-
if (!areaLists.get(i).contains(subArea)) {
166+
for (final ArrayList<SubArea> subAreas : areaLists) {
167+
if (!subAreas.contains(subArea)) {
166168
continue mainLoop;
167169
}
168170
}
169171
result.add(subArea);
170172
}
171-
172173
return result;
173174
}
174175

@@ -219,8 +220,8 @@ private SubArea(final int i, @Nonnull final Territory territory, @Nullable final
219220
if (latRange.getMax() != 90000000) {
220221
trimmedLatRange = trimRange(latRange);
221222
}
222-
final ArrayList<Range<Integer>> normalisedLonRange = normaliseRange(trimmedLonRange, lonBoundingRange);
223-
final ArrayList<Range<Integer>> normalisedLatRange = normaliseRange(trimmedLatRange, latBoundingRange);
223+
final ArrayList<Range<Integer>> normalisedLonRange = normaliseRange(trimmedLonRange, LON_BOUNDING_RANGE);
224+
final ArrayList<Range<Integer>> normalisedLatRange = normaliseRange(trimmedLatRange, LAT_BOUNDING_RANGE);
224225
if (territoryBounds == null) {
225226
boundedLonRange = normalisedLonRange;
226227
boundedLatRange = normalisedLatRange;

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

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -686,19 +686,19 @@ public static Territory fromString(
686686

687687
/**
688688
* 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}.
689+
* This is the same as {@link #toAlpha(AlphaFormat)} for {@link AlphaFormat#INTERNATIONAL}.
690690
*
691691
* @param alphabet Alphabet. May be null.
692692
* @return Territory name. Underscores have been replaced with dashes.
693693
*/
694694
@Nonnull
695695
public String toString(@Nullable final Alphabet alphabet) {
696-
return toNameFormat(NameFormat.INTERNATIONAL, alphabet);
696+
return toAlpha(AlphaFormat.INTERNATIONAL, alphabet);
697697
}
698698

699699
/**
700700
* 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}.
701+
* This is the same as {@link #toAlpha(AlphaFormat)} for {@link AlphaFormat#INTERNATIONAL}.
702702
*
703703
* @return Territory name. Underscores have been replaced with dashes.
704704
*/
@@ -711,7 +711,7 @@ public String toString() {
711711
/**
712712
* Enumeration that specifies the format for mapcodes.
713713
*/
714-
public enum NameFormat {
714+
public enum AlphaFormat {
715715
INTERNATIONAL, // Same as name() with underscores replaces with dashes.
716716
MINIMAL_UNAMBIGUOUS, // Minimal code, which is still unambiguous.
717717
MINIMAL // Minimal code, may be ambiguous, eg. RJ instead of IN-RJ.
@@ -725,15 +725,15 @@ public enum NameFormat {
725725
* @return Mapcode.
726726
*/
727727
@Nonnull
728-
public String toNameFormat(@Nonnull final NameFormat format, @Nullable final Alphabet alphabet) {
728+
public String toAlpha(@Nonnull final AlphaFormat format, @Nullable final Alphabet alphabet) {
729729
checkNonnull("format", format);
730730
String result = name().replace('_', '-');
731-
if (format != NameFormat.INTERNATIONAL) {
731+
if (format != AlphaFormat.INTERNATIONAL) {
732732
final int index = name().indexOf('_');
733733
if (index != -1) {
734734
assert name().length() > (index + 1);
735735
final String shortName = name().substring(index + 1);
736-
if ((format == NameFormat.MINIMAL) || (nameMap.get(shortName).size() == 1)) {
736+
if ((format == AlphaFormat.MINIMAL) || (nameMap.get(shortName).size() == 1)) {
737737
result = shortName;
738738
}
739739
}
@@ -749,8 +749,8 @@ public String toNameFormat(@Nonnull final NameFormat format, @Nullable final Alp
749749
}
750750

751751
@Nonnull
752-
public String toNameFormat(@Nonnull final NameFormat format) {
753-
return toNameFormat(format, null);
752+
public String toAlpha(@Nonnull final AlphaFormat format) {
753+
return toAlpha(format, null);
754754
}
755755

756756
/**

src/site/apt/ReleaseNotes.apt.vm

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,22 @@ Release Notes (Version ${project.version})
99
In any case, never depend on them for your own non-<<<SNAPSHOT>>> releases.
1010
#end
1111

12+
* 1.50.2
13+
14+
* Cleaned up Unicode handling a bit.
15+
16+
* Speed up of reading initialization data.
17+
18+
* Rename <<<toNameFormat>>> into <<<toAlphaFormat>>> and <<<NAME_FORMAT>>> to <<<ALPHA_FORMAT>>>.
19+
20+
[]
21+
1222
* 1.50.1
1323

1424
* Bugfix for mapcodes in IN-DD (in India).
1525

26+
[]
27+
1628
* 1.50
1729

1830
* Major release. This version is not backwards compatible with mapcode 1.4x: is has dropped support for

0 commit comments

Comments
 (0)