/**
* Copyright (C) 2016 eBusiness Information
*
* This file is part of OSM Contributor.
*
* OSM Contributor is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* OSM Contributor is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with OSM Contributor. If not, see <http://www.gnu.org/licenses/>.
*/
package io.jawg.osmcontributor.model.entities;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.field.ForeignCollectionField;
import com.j256.ormlite.table.DatabaseTable;
import com.mapbox.mapboxsdk.geometry.LatLng;
import org.joda.time.DateTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import io.jawg.osmcontributor.utils.core.MapElement;
import timber.log.Timber;
@DatabaseTable(tableName = Poi.TABLE_NAME)
public class Poi implements Cloneable, MapElement {
public static final String TABLE_NAME = "POI";
public static final String ID = "ID";
public static final String LONGITUDE = "LONGITUDE";
public static final String LATITUDE = "LATITUDE";
public static final String NAME = "NAME";
public static final String BACKEND_ID = "BACKEND_ID";
public static final String VERSION = "VERSION";
public static final String UPDATE_DATE = "UPDATE_DATE";
public static final String VISIBLE = "VISIBLE";
public static final String UPDATED = "UPDATED";
public static final String WAY = "WAY";
public static final String TO_DELETE = "TO_DELETE";
public static final String LEVEL = "LEVEL";
public static final String POI_TYPE_ID = "POI_TYPE_ID";
public static final String OLD = "OLD";
public static final String OLD_POI_ID = "OLD_POI_ID";
public enum State {
NORMAL, SELECTED, MOVING, NOT_SYNCED
}
public static State computeState(boolean selected, boolean edition, boolean needsSync) {
if (edition) {
return State.MOVING;
}
if (selected) {
return State.SELECTED;
}
if (needsSync) {
return State.NOT_SYNCED;
}
return State.NORMAL;
}
@DatabaseField(generatedId = true, columnName = ID)
private Long id;
@DatabaseField(columnName = LONGITUDE, canBeNull = false)
private Double longitude;
@DatabaseField(columnName = LATITUDE, canBeNull = false)
private Double latitude;
@DatabaseField(columnName = NAME)
private String name;
@DatabaseField(columnName = BACKEND_ID)
private String backendId;
@DatabaseField(columnName = VERSION)
private String version = "1";
@DatabaseField(columnName = UPDATE_DATE)
private DateTime updateDate;
@DatabaseField(columnName = VISIBLE)
private Boolean visible;
@DatabaseField(columnName = OLD)
private boolean old;
@DatabaseField(columnName = UPDATED, canBeNull = false)
private Boolean updated;
@DatabaseField(columnName = WAY, canBeNull = false)
private Boolean way = false;
@DatabaseField(columnName = TO_DELETE, canBeNull = false)
private Boolean toDelete = false;
@DatabaseField(columnName = LEVEL)
private String level;
@DatabaseField(columnName = OLD_POI_ID)
private Long oldPoiId;
@DatabaseField(foreign = true, columnName = POI_TYPE_ID, foreignAutoRefresh = true)
private PoiType type;
@ForeignCollectionField
private Collection<PoiTag> tags = new ArrayList<>();
@ForeignCollectionField(orderColumnName = PoiNodeRef.ORDINAL)
private Collection<PoiNodeRef> nodeRefs = new ArrayList<>();
private Set<Double> levels = null;
public Poi() {
}
public Long getId() {
return id;
}
public void setId(Long id) {
this.id = id;
}
public Double getLongitude() {
return longitude;
}
public void setLongitude(Double longitude) {
this.longitude = longitude;
}
public Double getLatitude() {
return latitude;
}
public void setLatitude(Double latitude) {
this.latitude = latitude;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getBackendId() {
return backendId;
}
public void setBackendId(String backendId) {
this.backendId = backendId;
}
public String getVersion() {
return version;
}
public void setVersion(String version) {
this.version = version;
}
public DateTime getUpdateDate() {
return updateDate;
}
public void setUpdateDate(DateTime updateDate) {
this.updateDate = updateDate;
}
public Boolean getVisible() {
return visible;
}
public void setVisible(Boolean visible) {
this.visible = visible;
}
public Boolean getUpdated() {
return updated;
}
public void setUpdated(Boolean updated) {
this.updated = updated;
}
public Boolean getWay() {
return way;
}
public void setWay(Boolean way) {
this.way = way;
}
public PoiType getType() {
return type;
}
public void setType(PoiType type) {
this.type = type;
}
public Collection<PoiTag> getTags() {
return tags;
}
public void setTags(Collection<PoiTag> tags) {
this.tags = tags;
}
public Collection<PoiNodeRef> getNodeRefs() {
return nodeRefs;
}
public void setNodeRefs(Collection<PoiNodeRef> nodeRefs) {
this.nodeRefs = nodeRefs;
}
public Boolean getToDelete() {
return toDelete;
}
public void setToDelete(Boolean toDelete) {
this.toDelete = toDelete;
}
public String getLevel() {
return level;
}
public void setLevel(String level) {
this.level = level;
}
public boolean getOld() {
return old;
}
public void setOld(boolean old) {
this.old = old;
}
public Long getOldPoiId() {
return oldPoiId;
}
public void setOldPoiId(Long oldPoiId) {
this.oldPoiId = oldPoiId;
}
public LatLng getPosition() {
return new LatLng(latitude, longitude);
}
//fill levels set if there isn't any levels adding level 0
private void initLevel() {
levels = getLevelsFromString();
if (levels.isEmpty()) {
levels.add(0d);
}
}
private Set<Double> getLevelsFromString() {
Set<Double> dlevels = new HashSet<>();
if (level == null) {
dlevels.add(0d);
} else {
// levels from OSM looks like "0.5;2;-2"
String[] levels = level.split(";");
//convert the string in a set of double
for (String strLevel : levels) {
try {
double dlevel = Double.parseDouble(strLevel);
dlevels.add(dlevel);
} catch (NumberFormatException e) {
dlevels.clear();
dlevels.add(0d);
}
}
}
return dlevels;
}
public void setLevel(Set<Double> level) {
if (this.levels != null) {
this.levels.clear();
} else {
this.levels = new HashSet<>();
}
levels.addAll(level);
setLevelTag(level);
}
//save the level in a tag "level"
private void setLevelTag(Set<Double> levels) {
if (!(levels.size() == 1 && levels.contains(0d))) {
boolean hasTagLevel = false;
String strLevels = "";
int i = 1;
for (Double d : levels) {
strLevels += d;
if (i != levels.size()) {
strLevels += ";";
}
i++;
}
for (PoiTag poiTag : tags) {
if ("level".equals(poiTag.getKey())) {
poiTag.setValue(strLevels);
hasTagLevel = true;
break;
}
}
if (!hasTagLevel) {
PoiTag tempPoiTag = new PoiTag();
tempPoiTag.setKey("level");
tempPoiTag.setValue(strLevels);
tempPoiTag.setPoi(this);
tags.add(tempPoiTag);
}
}
}
public boolean isAtLevel(Double level) {
initLevel();
return levels.contains(level);
}
public boolean isOnLevels(Double[] levels) {
initLevel();
for (Double d : levels) {
if (this.levels.contains(d)) {
return true;
}
}
return false;
}
@Override
public String toString() {
return "Poi{" +
"id=" + id +
", longitude=" + longitude +
", latitude=" + latitude +
", name='" + name + '\'' +
", backendId='" + backendId + '\'' +
", version='" + version + '\'' +
", updateDate=" + updateDate +
", visible=" + visible +
", old=" + old +
", updated=" + updated +
", way=" + way +
", toDelete=" + toDelete +
", level='" + level + '\'' +
", type=" + type +
", tags=" + tags +
", nodeRefs=" + nodeRefs +
", levels=" + levels +
'}';
}
public Map<String, String> getTagsMap() {
Map<String, String> hashMap = new HashMap<>();
if (tags != null) {
for (PoiTag poiTag : tags) {
hashMap.put(poiTag.getKey(), poiTag.getValue());
}
}
return hashMap;
}
public void applyChanges(Map<String, String> tagsMap) {
if (tags == null) {
tags = new ArrayList<>();
}
List<PoiTag> tagsToDelete = new ArrayList<>();
// Apply the new values to the existing tags
for (PoiTag poiTag : tags) {
String newValue = tagsMap.remove(poiTag.getKey());
if (newValue != null) {
if (newValue.trim().isEmpty()) {
tagsToDelete.add(poiTag);
if ("name".equalsIgnoreCase(poiTag.getKey())) {
setName("");
}
if ("level".equalsIgnoreCase(poiTag.getKey())) {
setLevel("");
}
} else if (!newValue.equals(poiTag.getValue())) {
poiTag.setValue(newValue);
}
}
}
tags.removeAll(tagsToDelete);
// Add the new tags to the Poi
for (Map.Entry<String, String> tagToAdd : tagsMap.entrySet()) {
//The default level is zero, we don't send this tag if it's the default value
if (!("level".equals(tagToAdd.getKey()) && "0".equals(tagToAdd.getValue()))) {
PoiTag tempPoiTag = new PoiTag();
tempPoiTag.setKey(tagToAdd.getKey());
tempPoiTag.setValue(tagToAdd.getValue());
tempPoiTag.setPoi(this);
tags.add(tempPoiTag);
}
}
// Set the name and level of the Poi to the value of the tags
for (PoiTag tag : tags) {
if ("name".equalsIgnoreCase(tag.getKey())) {
setName(tag.getValue());
}
if ("level".equalsIgnoreCase(tag.getKey())) {
setLevel(tag.getValue());
}
}
}
public boolean hasChanges(Map<String, String> tagsMap) {
if (tags == null) {
tags = new ArrayList<>();
}
int count = tagsMap.entrySet().size();
// Apply the new values to the existing tags
for (PoiTag poiTag : tags) {
String newValue = tagsMap.get(poiTag.getKey());
if (newValue != null) {
count--;
if (!newValue.equals(poiTag.getValue())) {
return true;
}
}
}
return count > 0;
}
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
Poi poi = (Poi) o;
return backendId != null ? (backendId.equals(poi.backendId) && old == poi.old) : id != null && id.equals(poi.id);
}
@Override
public int hashCode() {
return backendId != null ? backendId.hashCode() : super.hashCode();
}
public Poi getCopy() {
Poi poi;
try {
poi = (Poi) clone();
} catch (CloneNotSupportedException e) {
Timber.e(e, "could not clone Poi");
return null;
}
poi.setId(null);
List<PoiTag> poiTagsOld = new ArrayList<>();
for (PoiTag poiTag : getTags()) {
PoiTag poiTagOld = new PoiTag();
poiTagOld.setValue(poiTag.getValue());
poiTagOld.setKey(poiTag.getKey());
poiTagsOld.add(poiTagOld);
}
poi.setTags(poiTagsOld);
List<PoiNodeRef> poiNodeRefsOld = new ArrayList<>();
for (PoiNodeRef poiNodeRef : getNodeRefs()) {
PoiNodeRef poiNodeRefOld = new PoiNodeRef();
poiNodeRefOld.setLatitude(poiNodeRef.getLatitude());
poiNodeRefOld.setLongitude(poiNodeRef.getLongitude());
poiNodeRefOld.setNodeBackendId(poiNodeRef.getNodeBackendId());
poiNodeRefOld.setOrdinal(poiNodeRef.getOrdinal());
poiNodeRefOld.setUpdated(poiNodeRef.getUpdated());
poiNodeRefsOld.add(poiNodeRefOld);
}
poi.setNodeRefs(poiNodeRefsOld);
return poi;
}
}