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