diff --git a/OsmAnd-java/build.gradle b/OsmAnd-java/build.gradle index 62f3fe06d9..1e72f29fc3 100644 --- a/OsmAnd-java/build.gradle +++ b/OsmAnd-java/build.gradle @@ -103,6 +103,7 @@ dependencies { implementation 'org.apache.commons:commons-compress:1.17' implementation 'com.moparisthebest:junidecode:0.1.1' implementation 'com.vividsolutions:jts-core:1.14.0' + implementation 'com.google.openlocationcode:openlocationcode:1.0.4' // turn off for now //implementation 'com.atilika.kuromoji:kuromoji-ipadic:0.9.0' implementation 'net.sf.kxml:kxml2:2.1.8' diff --git a/OsmAnd-java/src/main/java/com/google/openlocationcode/OpenLocationCode.java b/OsmAnd-java/src/main/java/com/google/openlocationcode/OpenLocationCode.java deleted file mode 100644 index 32da135cdf..0000000000 --- a/OsmAnd-java/src/main/java/com/google/openlocationcode/OpenLocationCode.java +++ /dev/null @@ -1,618 +0,0 @@ -// Copyright 2014 Google Inc. All rights reserved. -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package com.google.openlocationcode; - -import java.math.BigDecimal; - -/** - * Convert locations to and from convenient short codes. - * - * Open Location Codes are short, ~10 character codes that can be used instead of street - * addresses. The codes can be generated and decoded offline, and use a reduced character set that - * minimises the chance of codes including words. - * - * This provides both object and static methods. - * - * Create an object with: - * OpenLocationCode code = new OpenLocationCode("7JVW52GR+2V"); - * OpenLocationCode code = new OpenLocationCode("52GR+2V"); - * OpenLocationCode code = new OpenLocationCode(27.175063, 78.042188); - * OpenLocationCode code = new OpenLocationCode(27.175063, 78.042188, 11); - * - * Once you have a code object, you can apply the other methods to it, such as to shorten: - * code.shorten(27.176, 78.05) - * - * Recover the nearest match (if the code was a short code): - * code.recover(27.176, 78.05) - * - * Or decode a code into its coordinates, returning a CodeArea object. - * code.decode() - * - * @author Jiri Semecky - * @author Doug Rinckes - */ -public final class OpenLocationCode { - - // Provides a normal precision code, approximately 14x14 meters. - public static final int CODE_PRECISION_NORMAL = 10; - - // The character set used to encode the values. - public static final String CODE_ALPHABET = "23456789CFGHJMPQRVWX"; - - // A separator used to break the code into two parts to aid memorability. - public static final char SEPARATOR = '+'; - - // The character used to pad codes. - public static final char PADDING_CHARACTER = '0'; - - // The number of characters to place before the separator. - private static final int SEPARATOR_POSITION = 8; - - // The max number of digits to process in a plus code. - public static final int MAX_DIGIT_COUNT = 15; - - // Note: The double type can't be used because of the rounding arithmetic due to floating point - // implementation. Eg. "8.95 - 8" can give result 0.9499999999999 instead of 0.95 which - // incorrectly classify the points on the border of a cell. Therefore all the calculation is done - // using BigDecimal. - - // The base to use to convert numbers to/from. - private static final BigDecimal ENCODING_BASE = new BigDecimal(CODE_ALPHABET.length()); - - // The maximum value for latitude in degrees. - private static final BigDecimal LATITUDE_MAX = new BigDecimal(90); - - // The maximum value for longitude in degrees. - private static final BigDecimal LONGITUDE_MAX = new BigDecimal(180); - - // Maximum code length using just lat/lng pair encoding. - private static final int PAIR_CODE_LENGTH = 10; - - // Number of columns in the grid refinement method. - private static final BigDecimal GRID_COLUMNS = new BigDecimal(4); - - // Number of rows in the grid refinement method. - private static final BigDecimal GRID_ROWS = new BigDecimal(5); - - /** - * Coordinates of a decoded Open Location Code. - * - *
The coordinates include the latitude and longitude of the lower left and upper right corners - * and the center of the bounding box for the area the code represents. - */ - public static class CodeArea { - - private final BigDecimal southLatitude; - private final BigDecimal westLongitude; - private final BigDecimal northLatitude; - private final BigDecimal eastLongitude; - - public CodeArea( - BigDecimal southLatitude, - BigDecimal westLongitude, - BigDecimal northLatitude, - BigDecimal eastLongitude) { - this.southLatitude = southLatitude; - this.westLongitude = westLongitude; - this.northLatitude = northLatitude; - this.eastLongitude = eastLongitude; - } - - public double getSouthLatitude() { - return southLatitude.doubleValue(); - } - - public double getWestLongitude() { - return westLongitude.doubleValue(); - } - - public double getLatitudeHeight() { - return northLatitude.subtract(southLatitude).doubleValue(); - } - - public double getLongitudeWidth() { - return eastLongitude.subtract(westLongitude).doubleValue(); - } - - public double getCenterLatitude() { - return southLatitude.add(northLatitude).doubleValue() / 2; - } - - public double getCenterLongitude() { - return westLongitude.add(eastLongitude).doubleValue() / 2; - } - - public double getNorthLatitude() { - return northLatitude.doubleValue(); - } - - public double getEastLongitude() { - return eastLongitude.doubleValue(); - } - } - - /** The current code for objects. */ - private final String code; - - /** - * Creates Open Location Code object for the provided code. - * @param code A valid OLC code. Can be a full code or a shortened code. - * @throws IllegalArgumentException when the passed code is not valid. - */ - public OpenLocationCode(String code) { - if (!isValidCode(code.toUpperCase())) { - throw new IllegalArgumentException( - "The provided code '" + code + "' is not a valid Open Location Code."); - } - this.code = code.toUpperCase(); - } - - /** - * Creates Open Location Code. - * @param latitude The latitude in decimal degrees. - * @param longitude The longitude in decimal degrees. - * @param codeLength The desired number of digits in the code. - * @throws IllegalArgumentException if the code length is not valid. - */ - public OpenLocationCode(double latitude, double longitude, int codeLength) { - // Limit the maximum number of digits in the code. - codeLength = Math.min(codeLength, MAX_DIGIT_COUNT); - // Check that the code length requested is valid. - if (codeLength < 4 || (codeLength < PAIR_CODE_LENGTH && codeLength % 2 == 1)) { - throw new IllegalArgumentException("Illegal code length " + codeLength); - } - // Ensure that latitude and longitude are valid. - latitude = clipLatitude(latitude); - longitude = normalizeLongitude(longitude); - - // Latitude 90 needs to be adjusted to be just less, so the returned code can also be decoded. - if (latitude == LATITUDE_MAX.doubleValue()) { - latitude = latitude - 0.9 * computeLatitudePrecision(codeLength); - } - - // Adjust latitude and longitude to be in positive number ranges. - // We add the max values when creating the BigDecimal (as opposed to creating a BigDecimal and - // then adding the value) to be consistent with the other implementations. - BigDecimal remainingLatitude = new BigDecimal(latitude + LATITUDE_MAX.doubleValue()); - BigDecimal remainingLongitude = new BigDecimal(longitude + LONGITUDE_MAX.doubleValue()); - - // Count how many digits have been created. - int generatedDigits = 0; - // Store the code. - StringBuilder codeBuilder = new StringBuilder(); - // The precisions are initially set to ENCODING_BASE^2 because they will be immediately - // divided. - BigDecimal latPrecision = ENCODING_BASE.multiply(ENCODING_BASE); - BigDecimal lngPrecision = ENCODING_BASE.multiply(ENCODING_BASE); - while (generatedDigits < codeLength) { - if (generatedDigits < PAIR_CODE_LENGTH) { - // Use the normal algorithm for the first set of digits. - latPrecision = latPrecision.divide(ENCODING_BASE); - lngPrecision = lngPrecision.divide(ENCODING_BASE); - BigDecimal latDigit = remainingLatitude.divide(latPrecision, 0, BigDecimal.ROUND_FLOOR); - BigDecimal lngDigit = remainingLongitude.divide(lngPrecision, 0, BigDecimal.ROUND_FLOOR); - remainingLatitude = remainingLatitude.subtract(latPrecision.multiply(latDigit)); - remainingLongitude = remainingLongitude.subtract(lngPrecision.multiply(lngDigit)); - codeBuilder.append(CODE_ALPHABET.charAt(latDigit.intValue())); - codeBuilder.append(CODE_ALPHABET.charAt(lngDigit.intValue())); - generatedDigits += 2; - } else { - // Use the 4x5 grid for remaining digits. - latPrecision = latPrecision.divide(GRID_ROWS); - lngPrecision = lngPrecision.divide(GRID_COLUMNS); - BigDecimal row = remainingLatitude.divide(latPrecision, 0, BigDecimal.ROUND_FLOOR); - BigDecimal col = remainingLongitude.divide(lngPrecision, 0, BigDecimal.ROUND_FLOOR); - remainingLatitude = remainingLatitude.subtract(latPrecision.multiply(row)); - remainingLongitude = remainingLongitude.subtract(lngPrecision.multiply(col)); - codeBuilder.append( - CODE_ALPHABET.charAt(row.intValue() * GRID_COLUMNS.intValue() + col.intValue())); - generatedDigits += 1; - } - // If we are at the separator position, add the separator. - if (generatedDigits == SEPARATOR_POSITION) { - codeBuilder.append(SEPARATOR); - } - } - // If the generated code is shorter than the separator position, pad the code and add the - // separator. - if (generatedDigits < SEPARATOR_POSITION) { - for (; generatedDigits < SEPARATOR_POSITION; generatedDigits++) { - codeBuilder.append(PADDING_CHARACTER); - } - codeBuilder.append(SEPARATOR); - } - this.code = codeBuilder.toString(); - } - - /** - * Creates Open Location Code with the default precision length. - * @param latitude The latitude in decimal degrees. - * @param longitude The longitude in decimal degrees. - */ - public OpenLocationCode(double latitude, double longitude) { - this(latitude, longitude, CODE_PRECISION_NORMAL); - } - - /** - * Returns the string representation of the code. - */ - public String getCode() { - return code; - } - - /** - * Encodes latitude/longitude into 10 digit Open Location Code. This method is equivalent to - * creating the OpenLocationCode object and getting the code from it. - * @param latitude The latitude in decimal degrees. - * @param longitude The longitude in decimal degrees. - */ - public static String encode(double latitude, double longitude) { - return new OpenLocationCode(latitude, longitude).getCode(); - } - - /** - * Encodes latitude/longitude into Open Location Code of the provided length. This method is - * equivalent to creating the OpenLocationCode object and getting the code from it. - * @param latitude The latitude in decimal degrees. - * @param longitude The longitude in decimal degrees. - */ - public static String encode(double latitude, double longitude, int codeLength) { - return new OpenLocationCode(latitude, longitude, codeLength).getCode(); - } - - /** - * Decodes {@link OpenLocationCode} object into {@link CodeArea} object encapsulating - * latitude/longitude bounding box. - */ - public CodeArea decode() { - if (!isFullCode(code)) { - throw new IllegalStateException( - "Method decode() could only be called on valid full codes, code was " + code + "."); - } - // Strip padding and separator characters out of the code. - String decoded = code.replace(String.valueOf(SEPARATOR), "") - .replace(String.valueOf(PADDING_CHARACTER), ""); - - int digit = 0; - // The precisions are initially set to ENCODING_BASE^2 because they will be immediately - // divided. - BigDecimal latPrecision = ENCODING_BASE.multiply(ENCODING_BASE); - BigDecimal lngPrecision = ENCODING_BASE.multiply(ENCODING_BASE); - // Save the coordinates. - BigDecimal southLatitude = BigDecimal.ZERO; - BigDecimal westLongitude = BigDecimal.ZERO; - - // Decode the digits. - while (digit < Math.min(decoded.length(), MAX_DIGIT_COUNT)) { - if (digit < PAIR_CODE_LENGTH) { - // Decode a pair of digits, the first being latitude and the second being longitude. - latPrecision = latPrecision.divide(ENCODING_BASE); - lngPrecision = lngPrecision.divide(ENCODING_BASE); - int digitVal = CODE_ALPHABET.indexOf(decoded.charAt(digit)); - southLatitude = southLatitude.add(latPrecision.multiply(new BigDecimal(digitVal))); - digitVal = CODE_ALPHABET.indexOf(decoded.charAt(digit + 1)); - westLongitude = westLongitude.add(lngPrecision.multiply(new BigDecimal(digitVal))); - digit += 2; - } else { - // Use the 4x5 grid for digits after 10. - int digitVal = CODE_ALPHABET.indexOf(decoded.charAt(digit)); - int row = (int) (digitVal / GRID_COLUMNS.intValue()); - int col = digitVal % GRID_COLUMNS.intValue(); - latPrecision = latPrecision.divide(GRID_ROWS); - lngPrecision = lngPrecision.divide(GRID_COLUMNS); - southLatitude = southLatitude.add(latPrecision.multiply(new BigDecimal(row))); - westLongitude = westLongitude.add(lngPrecision.multiply(new BigDecimal(col))); - digit += 1; - } - } - return new CodeArea( - southLatitude.subtract(LATITUDE_MAX), - westLongitude.subtract(LONGITUDE_MAX), - southLatitude.subtract(LATITUDE_MAX).add(latPrecision), - westLongitude.subtract(LONGITUDE_MAX).add(lngPrecision)); - } - - /** - * Decodes code representing Open Location Code into {@link CodeArea} object encapsulating - * latitude/longitude bounding box. - * - * @param code Open Location Code to be decoded. - * @throws IllegalArgumentException if the provided code is not a valid Open Location Code. - */ - public static CodeArea decode(String code) throws IllegalArgumentException { - return new OpenLocationCode(code).decode(); - } - - /** Returns whether this {@link OpenLocationCode} is a full Open Location Code. */ - public boolean isFull() { - return code.indexOf(SEPARATOR) == SEPARATOR_POSITION; - } - - /** Returns whether the provided Open Location Code is a full Open Location Code. */ - public static boolean isFull(String code) throws IllegalArgumentException { - return new OpenLocationCode(code).isFull(); - } - - /** Returns whether this {@link OpenLocationCode} is a short Open Location Code. */ - public boolean isShort() { - return code.indexOf(SEPARATOR) >= 0 && code.indexOf(SEPARATOR) < SEPARATOR_POSITION; - } - - /** Returns whether the provided Open Location Code is a short Open Location Code. */ - public static boolean isShort(String code) throws IllegalArgumentException { - return new OpenLocationCode(code).isShort(); - } - - /** - * Returns whether this {@link OpenLocationCode} is a padded Open Location Code, meaning that it - * contains less than 8 valid digits. - */ - private boolean isPadded() { - return code.indexOf(PADDING_CHARACTER) >= 0; - } - - /** - * Returns whether the provided Open Location Code is a padded Open Location Code, meaning that it - * contains less than 8 valid digits. - */ - public static boolean isPadded(String code) throws IllegalArgumentException { - return new OpenLocationCode(code).isPadded(); - } - - /** - * Returns short {@link OpenLocationCode} from the full Open Location Code created by removing - * four or six digits, depending on the provided reference point. It removes as many digits as - * possible. - */ - public OpenLocationCode shorten(double referenceLatitude, double referenceLongitude) { - if (!isFull()) { - throw new IllegalStateException("shorten() method could only be called on a full code."); - } - if (isPadded()) { - throw new IllegalStateException("shorten() method can not be called on a padded code."); - } - - CodeArea codeArea = decode(); - double range = Math.max( - Math.abs(referenceLatitude - codeArea.getCenterLatitude()), - Math.abs(referenceLongitude - codeArea.getCenterLongitude())); - // We are going to check to see if we can remove three pairs, two pairs or just one pair of - // digits from the code. - for (int i = 4; i >= 1; i--) { - // Check if we're close enough to shorten. The range must be less than 1/2 - // the precision to shorten at all, and we want to allow some safety, so - // use 0.3 instead of 0.5 as a multiplier. - if (range < (computeLatitudePrecision(i * 2) * 0.3)) { - // We're done. - return new OpenLocationCode(code.substring(i * 2)); - } - } - throw new IllegalArgumentException( - "Reference location is too far from the Open Location Code center."); - } - - /** - * Returns an {@link OpenLocationCode} object representing a full Open Location Code from this - * (short) Open Location Code, given the reference location. - */ - public OpenLocationCode recover(double referenceLatitude, double referenceLongitude) { - if (isFull()) { - // Note: each code is either full xor short, no other option. - return this; - } - referenceLatitude = clipLatitude(referenceLatitude); - referenceLongitude = normalizeLongitude(referenceLongitude); - - int digitsToRecover = SEPARATOR_POSITION - code.indexOf(SEPARATOR); - // The precision (height and width) of the missing prefix in degrees. - double prefixPrecision = Math.pow(ENCODING_BASE.intValue(), 2 - (digitsToRecover / 2)); - - // Use the reference location to generate the prefix. - String recoveredPrefix = - new OpenLocationCode(referenceLatitude, referenceLongitude) - .getCode() - .substring(0, digitsToRecover); - // Combine the prefix with the short code and decode it. - OpenLocationCode recovered = new OpenLocationCode(recoveredPrefix + code); - CodeArea recoveredCodeArea = recovered.decode(); - // Work out whether the new code area is too far from the reference location. If it is, we - // move it. It can only be out by a single precision step. - double recoveredLatitude = recoveredCodeArea.getCenterLatitude(); - double recoveredLongitude = recoveredCodeArea.getCenterLongitude(); - - // Move the recovered latitude by one precision up or down if it is too far from the reference, - // unless doing so would lead to an invalid latitude. - double latitudeDiff = recoveredLatitude - referenceLatitude; - if (latitudeDiff > prefixPrecision / 2 - && recoveredLatitude - prefixPrecision > -LATITUDE_MAX.intValue()) { - recoveredLatitude -= prefixPrecision; - } else if (latitudeDiff < -prefixPrecision / 2 - && recoveredLatitude + prefixPrecision < LATITUDE_MAX.intValue()) { - recoveredLatitude += prefixPrecision; - } - - // Move the recovered longitude by one precision up or down if it is too far from the - // reference. - double longitudeDiff = recoveredCodeArea.getCenterLongitude() - referenceLongitude; - if (longitudeDiff > prefixPrecision / 2) { - recoveredLongitude -= prefixPrecision; - } else if (longitudeDiff < -prefixPrecision / 2) { - recoveredLongitude += prefixPrecision; - } - - return new OpenLocationCode( - recoveredLatitude, recoveredLongitude, recovered.getCode().length() - 1); - } - - /** - * Returns whether the bounding box specified by the Open Location Code contains provided point. - */ - public boolean contains(double latitude, double longitude) { - CodeArea codeArea = decode(); - return codeArea.getSouthLatitude() <= latitude - && latitude < codeArea.getNorthLatitude() - && codeArea.getWestLongitude() <= longitude - && longitude < codeArea.getEastLongitude(); - } - - @Override - public boolean equals(Object o) { - if (this == o) { - return true; - } - if (o == null || getClass() != o.getClass()) { - return false; - } - OpenLocationCode that = (OpenLocationCode) o; - return code == that.code || code != null && code.equals(that.code); - } - - @Override - public int hashCode() { - return code != null ? code.hashCode() : 0; - } - - @Override - public String toString() { - return getCode(); - } - - // Exposed static helper methods. - - /** Returns whether the provided string is a valid Open Location code. */ - public static boolean isValidCode(String code) { - if (code == null || code.length() < 2) { - return false; - } - code = code.toUpperCase(); - - // There must be exactly one separator. - int separatorPosition = code.indexOf(SEPARATOR); - if (separatorPosition == -1) { - return false; - } - if (separatorPosition != code.lastIndexOf(SEPARATOR)) { - return false; - } - - if (separatorPosition % 2 != 0) { - return false; - } - - // Check first two characters: only some values from the alphabet are permitted. - if (separatorPosition == 8) { - // First latitude character can only have first 9 values. - Integer index0 = CODE_ALPHABET.indexOf(code.charAt(0)); - if (index0 == null || index0 > 8) { - return false; - } - - // First longitude character can only have first 18 values. - Integer index1 = CODE_ALPHABET.indexOf(code.charAt(1)); - if (index1 == null || index1 > 17) { - return false; - } - } - - // Check the characters before the separator. - boolean paddingStarted = false; - for (int i = 0; i < separatorPosition; i++) { - if (paddingStarted) { - // Once padding starts, there must not be anything but padding. - if (code.charAt(i) != PADDING_CHARACTER) { - return false; - } - continue; - } - if (CODE_ALPHABET.indexOf(code.charAt(i)) != -1) { - continue; - } - if (PADDING_CHARACTER == code.charAt(i)) { - paddingStarted = true; - // Padding can start on even character: 2, 4 or 6. - if (i != 2 && i != 4 && i != 6) { - return false; - } - continue; - } - return false; // Illegal character. - } - - // Check the characters after the separator. - if (code.length() > separatorPosition + 1) { - if (paddingStarted) { - return false; - } - // Only one character after separator is forbidden. - if (code.length() == separatorPosition + 2) { - return false; - } - for (int i = separatorPosition + 1; i < code.length(); i++) { - if (CODE_ALPHABET.indexOf(code.charAt(i)) == -1) { - return false; - } - } - } - - return true; - } - - /** Returns if the code is a valid full Open Location Code. */ - public static boolean isFullCode(String code) { - try { - return new OpenLocationCode(code).isFull(); - } catch (IllegalArgumentException e) { - return false; - } - } - - /** Returns if the code is a valid short Open Location Code. */ - public static boolean isShortCode(String code) { - try { - return new OpenLocationCode(code).isShort(); - } catch (IllegalArgumentException e) { - return false; - } - } - - // Private static methods. - - private static double clipLatitude(double latitude) { - return Math.min(Math.max(latitude, -LATITUDE_MAX.intValue()), LATITUDE_MAX.intValue()); - } - - private static double normalizeLongitude(double longitude) { - while (longitude < -LONGITUDE_MAX.intValue()) { - longitude = longitude + LONGITUDE_MAX.intValue() * 2; - } - while (longitude >= LONGITUDE_MAX.intValue()) { - longitude = longitude - LONGITUDE_MAX.intValue() * 2; - } - return longitude; - } - - /** - * Compute the latitude precision value for a given code length. Lengths <= 10 have the same - * precision for latitude and longitude, but lengths > 10 have different precisions due to the - * grid method having fewer columns than rows. Copied from the JS implementation. - */ - private static double computeLatitudePrecision(int codeLength) { - if (codeLength <= CODE_PRECISION_NORMAL) { - return Math.pow(ENCODING_BASE.intValue(), Math.floor(codeLength / -2 + 2)); - } - return Math.pow(ENCODING_BASE.intValue(), -3) - / Math.pow(GRID_ROWS.intValue(), codeLength - PAIR_CODE_LENGTH); - } -} \ No newline at end of file diff --git a/OsmAnd/build.gradle b/OsmAnd/build.gradle index 7ec6764849..c661bd9e07 100644 --- a/OsmAnd/build.gradle +++ b/OsmAnd/build.gradle @@ -394,6 +394,7 @@ dependencies { implementation 'com.moparisthebest:junidecode:0.1.1' implementation 'org.immutables:gson:2.5.0' implementation 'com.vividsolutions:jts-core:1.14.0' + implementation 'com.google.openlocationcode:openlocationcode:1.0.4' // turn off for now //implementation 'com.atilika.kuromoji:kuromoji-ipadic:0.9.0' implementation 'com.squareup.picasso:picasso:2.71828' diff --git a/OsmAnd/src/net/osmand/data/PointDescription.java b/OsmAnd/src/net/osmand/data/PointDescription.java index a87635f3a7..a1ce8b5758 100644 --- a/OsmAnd/src/net/osmand/data/PointDescription.java +++ b/OsmAnd/src/net/osmand/data/PointDescription.java @@ -5,11 +5,7 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import com.google.openlocationcode.OpenLocationCode; -import com.jwetherell.openmap.common.LatLonPoint; -import com.jwetherell.openmap.common.UTMPoint; -import java.util.LinkedHashMap; -import java.util.Map; import net.osmand.LocationConvert; import net.osmand.plus.OsmAndFormatter; import net.osmand.plus.OsmandApplication; @@ -18,6 +14,9 @@ import net.osmand.plus.R; import net.osmand.plus.activities.MapActivity; import net.osmand.util.Algorithms; +import java.util.LinkedHashMap; +import java.util.Map; + public class PointDescription { private String type = ""; private String name = "";