001// License: GPL. For details, see LICENSE file. 002package org.openstreetmap.josm.gui.dialogs; 003 004import static org.openstreetmap.josm.tools.I18n.tr; 005import static org.openstreetmap.josm.tools.I18n.trn; 006 007import java.util.Arrays; 008import java.util.List; 009 010import org.openstreetmap.josm.data.conflict.Conflict; 011import org.openstreetmap.josm.data.coor.EastNorth; 012import org.openstreetmap.josm.data.coor.ILatLon; 013import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat; 014import org.openstreetmap.josm.data.osm.BBox; 015import org.openstreetmap.josm.data.osm.DataSet; 016import org.openstreetmap.josm.data.osm.INode; 017import org.openstreetmap.josm.data.osm.IPrimitive; 018import org.openstreetmap.josm.data.osm.IRelation; 019import org.openstreetmap.josm.data.osm.IRelationMember; 020import org.openstreetmap.josm.data.osm.IWay; 021import org.openstreetmap.josm.data.osm.OsmData; 022import org.openstreetmap.josm.data.osm.OsmPrimitive; 023import org.openstreetmap.josm.data.projection.ProjectionRegistry; 024import org.openstreetmap.josm.data.projection.proj.TransverseMercator; 025import org.openstreetmap.josm.data.projection.proj.TransverseMercator.Hemisphere; 026import org.openstreetmap.josm.tools.Geometry; 027import org.openstreetmap.josm.tools.Pair; 028import org.openstreetmap.josm.tools.Utils; 029import org.openstreetmap.josm.tools.date.DateUtils; 030 031/** 032 * Textual representation of primitive contents, used in {@code InspectPrimitiveDialog}. 033 * @since 10198 034 */ 035public class InspectPrimitiveDataText { 036 private static final String INDENT = " "; 037 private static final char NL = '\n'; 038 039 private final StringBuilder s = new StringBuilder(); 040 private final OsmData<?, ?, ?, ?> ds; 041 042 InspectPrimitiveDataText(OsmData<?, ?, ?, ?> ds) { 043 this.ds = ds; 044 } 045 046 private InspectPrimitiveDataText add(String title, String... values) { 047 s.append(INDENT).append(title); 048 for (String v : values) { 049 s.append(v); 050 } 051 s.append(NL); 052 return this; 053 } 054 055 private static String getNameAndId(String name, long id) { 056 if (name != null) { 057 return name + tr(" ({0})", /* sic to avoid thousand separators */ Long.toString(id)); 058 } else { 059 return Long.toString(id); 060 } 061 } 062 063 /** 064 * Adds a new OSM primitive. 065 * @param o primitive to add 066 */ 067 public void addPrimitive(IPrimitive o) { 068 069 addHeadline(o); 070 071 if (!(o.getDataSet() != null && o.getDataSet().getPrimitiveById(o) != null)) { 072 s.append(NL).append(INDENT).append(tr("not in data set")).append(NL); 073 return; 074 } 075 if (o.isIncomplete()) { 076 s.append(NL).append(INDENT).append(tr("incomplete")).append(NL); 077 return; 078 } 079 s.append(NL); 080 081 addState(o); 082 addCommon(o); 083 addAttributes(o); 084 addSpecial(o); 085 addReferrers(s, o); 086 if (o instanceof OsmPrimitive) { 087 addConflicts((OsmPrimitive) o); 088 } 089 s.append(NL); 090 } 091 092 void addHeadline(IPrimitive o) { 093 addType(o); 094 addNameAndId(o); 095 } 096 097 void addType(IPrimitive o) { 098 if (o instanceof INode) { 099 s.append(tr("Node: ")); 100 } else if (o instanceof IWay) { 101 s.append(tr("Way: ")); 102 } else if (o instanceof IRelation) { 103 s.append(tr("Relation: ")); 104 } 105 } 106 107 void addNameAndId(IPrimitive o) { 108 String name = o.get("name"); 109 if (name == null) { 110 s.append(o.getUniqueId()); 111 } else { 112 s.append(getNameAndId(name, o.getUniqueId())); 113 } 114 } 115 116 void addState(IPrimitive o) { 117 StringBuilder sb = new StringBuilder(INDENT); 118 /* selected state is left out: not interesting as it is always selected */ 119 if (o.isDeleted()) { 120 sb.append(tr("deleted")).append(INDENT); 121 } 122 if (!o.isVisible()) { 123 sb.append(tr("deleted-on-server")).append(INDENT); 124 } 125 if (o.isModified()) { 126 sb.append(tr("modified")).append(INDENT); 127 } 128 if (o.isDisabledAndHidden()) { 129 sb.append(tr("filtered/hidden")).append(INDENT); 130 } 131 if (o.isDisabled()) { 132 sb.append(tr("filtered/disabled")).append(INDENT); 133 } 134 if (o.hasDirectionKeys()) { 135 if (o.reversedDirection()) { 136 sb.append(tr("has direction keys (reversed)")).append(INDENT); 137 } else { 138 sb.append(tr("has direction keys")).append(INDENT); 139 } 140 } 141 String state = sb.toString().trim(); 142 if (!state.isEmpty()) { 143 add(tr("State: "), sb.toString().trim()); 144 } 145 } 146 147 void addCommon(IPrimitive o) { 148 add(tr("Data Set: "), Integer.toHexString(o.getDataSet().hashCode())); 149 add(tr("Edited at: "), o.isTimestampEmpty() ? tr("<new object>") 150 : DateUtils.fromTimestamp(o.getRawTimestamp())); 151 add(tr("Edited by: "), o.getUser() == null ? tr("<new object>") 152 : getNameAndId(o.getUser().getName(), o.getUser().getId())); 153 add(tr("Version:"), " ", Integer.toString(o.getVersion())); 154 add(tr("In changeset: "), Integer.toString(o.getChangesetId())); 155 } 156 157 void addAttributes(IPrimitive o) { 158 if (o.hasKeys()) { 159 add(tr("Tags: ")); 160 for (String key : o.keySet()) { 161 s.append(INDENT).append(INDENT); 162 s.append(String.format("\"%s\"=\"%s\"%n", key, o.get(key))); 163 } 164 } 165 } 166 167 void addSpecial(IPrimitive o) { 168 if (o instanceof INode) { 169 addCoordinates((INode) o); 170 } else if (o instanceof IWay) { 171 addBbox(o); 172 add(tr("Centroid: "), 173 toStringCSV(", ", ProjectionRegistry.getProjection().eastNorth2latlon( 174 Geometry.getCentroid(((IWay<?>) o).getNodes())))); 175 addWayNodes((IWay<?>) o); 176 } else if (o instanceof IRelation) { 177 addBbox(o); 178 addRelationMembers((IRelation<?>) o); 179 } 180 } 181 182 void addRelationMembers(IRelation<?> r) { 183 add(trn("{0} Member: ", "{0} Members: ", r.getMembersCount(), r.getMembersCount())); 184 for (IRelationMember<?> m : r.getMembers()) { 185 s.append(INDENT).append(INDENT); 186 addHeadline(m.getMember()); 187 s.append(tr(" as \"{0}\"", m.getRole())); 188 s.append(NL); 189 } 190 } 191 192 void addWayNodes(IWay<?> w) { 193 add(tr("{0} Nodes: ", w.getNodesCount())); 194 for (INode n : w.getNodes()) { 195 s.append(INDENT).append(INDENT); 196 addNameAndId(n); 197 s.append(NL); 198 } 199 } 200 201 void addBbox(IPrimitive o) { 202 BBox bbox = o.getBBox(); 203 if (bbox != null) { 204 add(tr("Bounding box: "), bbox.toStringCSV(", ")); 205 EastNorth bottomRigth = bbox.getBottomRight().getEastNorth(ProjectionRegistry.getProjection()); 206 EastNorth topLeft = bbox.getTopLeft().getEastNorth(ProjectionRegistry.getProjection()); 207 add(tr("Bounding box (projected): "), 208 Double.toString(topLeft.east()), ", ", 209 Double.toString(bottomRigth.north()), ", ", 210 Double.toString(bottomRigth.east()), ", ", 211 Double.toString(topLeft.north())); 212 add(tr("Center of bounding box: "), toStringCSV(", ", bbox.getCenter())); 213 } 214 } 215 216 void addCoordinates(INode n) { 217 if (n.isLatLonKnown()) { 218 add(tr("Coordinates:"), " ", 219 Double.toString(n.lat()), ", ", 220 Double.toString(n.lon())); 221 EastNorth en = n.getEastNorth(); 222 add(tr("Coordinates (projected): "), 223 Double.toString(en.east()), ", ", 224 Double.toString(en.north())); 225 Pair<Integer, Hemisphere> utmZone = TransverseMercator.locateUtmZone(n.getCoor()); 226 String utmLabel = tr("UTM Zone"); 227 add(utmLabel, utmLabel.endsWith(":") ? " " : ": ", Integer.toString(utmZone.a), utmZone.b.name().substring(0, 1)); 228 } 229 } 230 231 void addReferrers(StringBuilder s, IPrimitive o) { 232 List<? extends IPrimitive> refs = o.getReferrers(); 233 if (!refs.isEmpty()) { 234 add(tr("Part of: ")); 235 for (IPrimitive p : refs) { 236 s.append(INDENT).append(INDENT); 237 addHeadline(p); 238 s.append(NL); 239 } 240 } 241 } 242 243 void addConflicts(OsmPrimitive o) { 244 Conflict<?> c = ((DataSet) ds).getConflicts().getConflictForMy(o); 245 if (c != null) { 246 add(tr("In conflict with: ")); 247 addNameAndId(c.getTheir()); 248 } 249 } 250 251 /** 252 * Returns lat/lon coordinate in human-readable format separated by {@code separator}. 253 * @param separator values separator 254 * @param ll the lat/lon 255 * @return String in the format {@code "1.23456[separator]2.34567"} 256 */ 257 private static String toStringCSV(String separator, ILatLon ll) { 258 return Utils.join(separator, Arrays.asList( 259 DecimalDegreesCoordinateFormat.INSTANCE.latToString(ll), 260 DecimalDegreesCoordinateFormat.INSTANCE.lonToString(ll) 261 )); 262 } 263 264 @Override 265 public String toString() { 266 return s.toString(); 267 } 268}