package cgeo.geocaching.models; import cgeo.geocaching.enumerations.CoordinatesType; import cgeo.geocaching.enumerations.LoadFlags; import cgeo.geocaching.enumerations.WaypointType; import cgeo.geocaching.location.Geopoint; import cgeo.geocaching.location.GeopointParser; import cgeo.geocaching.maps.mapsforge.v6.caches.GeoitemRef; import cgeo.geocaching.storage.DataStore; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import java.util.Collection; import java.util.Comparator; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Locale; import java.util.Map; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.tuple.ImmutablePair; public class Waypoint implements IWaypoint { public static final String PREFIX_OWN = "OWN"; private static final int ORDER_UNDEFINED = -2; private int id = -1; private String geocode = "geocode"; private WaypointType waypointType = WaypointType.WAYPOINT; private String prefix = ""; private String lookup = ""; private String name = ""; private Geopoint coords = null; private String note = ""; private String userNote = ""; private int cachedOrder = ORDER_UNDEFINED; private boolean own = false; private boolean visited = false; private boolean originalCoordsEmpty = false; /** * require name and type for every waypoint * */ public Waypoint(final String name, final WaypointType type, final boolean own) { this.name = name; this.waypointType = type; this.own = own; } /** * copy constructor * */ public Waypoint(final Waypoint other) { merge(other); this.waypointType = other.waypointType; id = -1; } public void merge(final Waypoint old) { if (StringUtils.isBlank(prefix)) { setPrefix(old.prefix); } if (StringUtils.isBlank(lookup)) { lookup = old.lookup; } if (StringUtils.isBlank(name)) { name = old.name; } if (coords == null) { coords = old.coords; } if (StringUtils.isBlank(note)) { note = old.note; } if (StringUtils.isBlank(userNote)) { userNote = old.userNote; } if (StringUtils.equals(note, userNote)) { userNote = ""; } if (id < 0) { id = old.id; } visited = old.visited; } public static void mergeWayPoints(final List<Waypoint> newPoints, final List<Waypoint> oldPoints, final boolean forceMerge) { // Build a map of new waypoints for faster subsequent lookups final Map<String, Waypoint> newPrefixes = new HashMap<>(newPoints.size()); for (final Waypoint waypoint : newPoints) { newPrefixes.put(waypoint.getPrefix(), waypoint); } // Copy user modified details of the old waypoints over the new ones for (final Waypoint oldWaypoint : oldPoints) { final String prefix = oldWaypoint.getPrefix(); if (newPrefixes.containsKey(prefix)) { newPrefixes.get(prefix).merge(oldWaypoint); } else if (oldWaypoint.isUserDefined() || forceMerge) { newPoints.add(oldWaypoint); } } } public boolean isUserDefined() { return own || waypointType == WaypointType.OWN; } public void setUserDefined() { own = true; setPrefix(PREFIX_OWN); } private int order() { if (cachedOrder == ORDER_UNDEFINED) { cachedOrder = waypointType.order; } return cachedOrder; } @NonNull public String getPrefix() { return prefix; } public void setPrefix(@NonNull final String prefix) { this.prefix = prefix; cachedOrder = ORDER_UNDEFINED; } @NonNull public String getUrl() { return "https://www.geocaching.com/seek/cache_details.aspx?wp=" + geocode; } @Override public int getId() { return id; } public void setId(final int id) { this.id = id; } @Override public String getGeocode() { return geocode; } public void setGeocode(final String geocode) { this.geocode = StringUtils.upperCase(geocode); } @Override public WaypointType getWaypointType() { return waypointType; } public String getLookup() { return lookup; } public void setLookup(final String lookup) { this.lookup = lookup; } @Override public String getName() { return name; } public void setName(final String name) { this.name = name; } @Nullable @Override public Geopoint getCoords() { return coords; } public void setCoords(final Geopoint coords) { this.coords = coords; } public String getNote() { return note; } public void setNote(final String note) { this.note = note; } @Override public String toString() { return name + " " + waypointType.getL10n(); } /** * Checks whether a given waypoint is a final and has coordinates * * @return True - waypoint is final and has coordinates, False - otherwise */ public boolean isFinalWithCoords() { return waypointType == WaypointType.FINAL && coords != null; } @Override public CoordinatesType getCoordType() { return CoordinatesType.WAYPOINT; } public void setVisited(final boolean visited) { this.visited = visited; } public boolean isVisited() { return visited; } public int getStaticMapsHashcode() { long hash = 0; if (coords != null) { hash = coords.hashCode(); } hash ^= waypointType.markerId; return (int) hash; } /** * Sort waypoints by their probable order (e.g. parking first, final last). */ public static final Comparator<? super Waypoint> WAYPOINT_COMPARATOR = new Comparator<Waypoint>() { @Override public int compare(final Waypoint left, final Waypoint right) { return left.order() - right.order(); } }; /** * Delegates the creation of the waypoint-id for gpx-export to the waypoint * */ public String getGpxId() { String gpxId = prefix; if (StringUtils.isNotBlank(geocode)) { final Geocache cache = DataStore.loadCache(geocode, LoadFlags.LOAD_CACHE_OR_DB); if (cache != null) { gpxId = cache.getWaypointGpxId(prefix); } } return gpxId; } /** * Detect coordinates in the given text and converts them to user defined waypoints. * Works by rule of thumb. * * @param text Text to parse for waypoints * @param namePrefix Prefix of the name of the waypoint * @return a collection of found waypoints */ public static Collection<Waypoint> parseWaypoints(@NonNull final String text, @NonNull final String namePrefix) { final List<Waypoint> waypoints = new LinkedList<>(); final Collection<ImmutablePair<Geopoint, Integer>> matches = GeopointParser.parseAll(text); int count = 1; for (final ImmutablePair<Geopoint, Integer> match : matches) { final Geopoint point = match.getLeft(); final Integer start = match.getRight(); final String name = namePrefix + " " + count; final String potentialWaypointType = text.substring(Math.max(0, start - 15)); final Waypoint waypoint = new Waypoint(name, parseWaypointType(potentialWaypointType), true); waypoint.setCoords(point); waypoints.add(waypoint); count++; } return waypoints; } /** * Detect waypoint types in the personal note text. It works by rule of thumb only. */ private static WaypointType parseWaypointType(final String input) { final String lowerInput = StringUtils.substring(input, 0, 20).toLowerCase(Locale.getDefault()); for (final WaypointType wpType : WaypointType.values()) { if (lowerInput.contains(wpType.getL10n().toLowerCase(Locale.getDefault()))) { return wpType; } if (lowerInput.contains(wpType.id)) { return wpType; } if (lowerInput.contains(wpType.name().toLowerCase(Locale.US))) { return wpType; } } return WaypointType.WAYPOINT; } public GeoitemRef getGeoitemRef() { return new GeoitemRef(getGpxId(), getCoordType(), getGeocode(), getId(), getName(), getWaypointType().markerId); } public String getUserNote() { return userNote; } public void setUserNote(final String userNote) { this.userNote = userNote; } public boolean isOriginalCoordsEmpty() { return originalCoordsEmpty; } public void setOriginalCoordsEmpty(final boolean originalCoordsEmpty) { this.originalCoordsEmpty = originalCoordsEmpty; } }