source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDataText.java@ 19078

Last change on this file since 19078 was 19078, checked in by taylor.smock, 4 months ago

Fix #4142: Track fully downloaded objects (patch by stoecker, GerdP, and myself)

The serialization move from PrimitiveData to AbstractPrimitive should be
reverted prior to 24.05 (see #23677).

The serialization move was required since we want to ensure that all downstream
users of AbstractPrimitive were not using the flags field, which was done by
making the field private instead of protected. They may still be using that
field (via updateFlags) which would not be caught by compile-time or runtime
errors.

Additionally, a good chunk of common functionality was moved up from
OsmPrimitive, even though much of it wasn't useful for PrimitiveData.

  • Property svn:eol-style set to native
File size: 11.0 KB
Line 
1// License: GPL. For details, see LICENSE file.
2package org.openstreetmap.josm.gui.dialogs;
3
4import static org.openstreetmap.josm.tools.I18n.tr;
5import static org.openstreetmap.josm.tools.I18n.trn;
6
7import java.util.Arrays;
8import java.util.List;
9import java.util.stream.Collectors;
10import java.util.stream.Stream;
11
12import org.openstreetmap.josm.data.SystemOfMeasurement;
13import org.openstreetmap.josm.data.conflict.Conflict;
14import org.openstreetmap.josm.data.coor.EastNorth;
15import org.openstreetmap.josm.data.coor.ILatLon;
16import org.openstreetmap.josm.data.coor.LatLon;
17import org.openstreetmap.josm.data.coor.conversion.AbstractCoordinateFormat;
18import org.openstreetmap.josm.data.coor.conversion.DecimalDegreesCoordinateFormat;
19import org.openstreetmap.josm.data.coor.conversion.ProjectedCoordinateFormat;
20import org.openstreetmap.josm.data.osm.BBox;
21import org.openstreetmap.josm.data.osm.DataSet;
22import org.openstreetmap.josm.data.osm.INode;
23import org.openstreetmap.josm.data.osm.IPrimitive;
24import org.openstreetmap.josm.data.osm.IRelation;
25import org.openstreetmap.josm.data.osm.IRelationMember;
26import org.openstreetmap.josm.data.osm.IWay;
27import org.openstreetmap.josm.data.osm.OsmData;
28import org.openstreetmap.josm.data.osm.OsmPrimitive;
29import org.openstreetmap.josm.data.osm.Relation;
30import org.openstreetmap.josm.data.osm.Way;
31import org.openstreetmap.josm.data.projection.ProjectionRegistry;
32import org.openstreetmap.josm.data.projection.proj.TransverseMercator;
33import org.openstreetmap.josm.data.projection.proj.TransverseMercator.Hemisphere;
34import org.openstreetmap.josm.tools.Geometry;
35import org.openstreetmap.josm.tools.Pair;
36import org.openstreetmap.josm.tools.Utils;
37
38/**
39 * Textual representation of primitive contents, used in {@code InspectPrimitiveDialog}.
40 * @since 10198
41 */
42public class InspectPrimitiveDataText {
43 private static final String INDENT = " ";
44 private static final char NL = '\n';
45
46 private final StringBuilder s = new StringBuilder();
47 private final OsmData<?, ?, ?, ?> ds;
48
49 InspectPrimitiveDataText(OsmData<?, ?, ?, ?> ds) {
50 this.ds = ds;
51 }
52
53 private InspectPrimitiveDataText add(String title, String... values) {
54 s.append(INDENT).append(title);
55 for (String v : values) {
56 s.append(v);
57 }
58 s.append(NL);
59 return this;
60 }
61
62 private static String getNameAndId(String name, long id) {
63 if (name != null) {
64 return name + tr(" ({0})", /* sic to avoid thousand separators */ Long.toString(id));
65 } else {
66 return Long.toString(id);
67 }
68 }
69
70 /**
71 * Adds a new OSM primitive.
72 * @param o primitive to add
73 */
74 public void addPrimitive(IPrimitive o) {
75
76 addHeadline(o);
77
78 if (!(o.getDataSet() != null && o.getDataSet().getPrimitiveById(o) != null)) {
79 s.append(NL).append(INDENT).append(tr("not in data set")).append(NL);
80 return;
81 }
82 if (o.isIncomplete()) {
83 s.append(NL).append(INDENT).append(tr("incomplete")).append(NL);
84 return;
85 }
86 s.append(NL);
87
88 addState(o);
89 addCommon(o);
90 addAttributes(o);
91 addSpecial(o);
92 addReferrers(s, o);
93 if (o instanceof OsmPrimitive) {
94 addConflicts((OsmPrimitive) o);
95 }
96 s.append(NL);
97 }
98
99 void addHeadline(IPrimitive o) {
100 addType(o);
101 addNameAndId(o);
102 }
103
104 void addType(IPrimitive o) {
105 if (o instanceof INode) {
106 s.append(tr("Node: "));
107 } else if (o instanceof IWay) {
108 s.append(tr("Way: "));
109 } else if (o instanceof IRelation) {
110 s.append(tr("Relation: "));
111 }
112 }
113
114 void addNameAndId(IPrimitive o) {
115 String name = o.get("name");
116 if (name == null) {
117 s.append(o.getUniqueId());
118 } else {
119 s.append(getNameAndId(name, o.getUniqueId()));
120 }
121 }
122
123 void addState(IPrimitive o) {
124 StringBuilder sb = new StringBuilder(INDENT);
125 /* selected state is left out: not interesting as it is always selected */
126 if (o.isDeleted()) {
127 sb.append(tr("deleted")).append(INDENT);
128 }
129 if (!o.isVisible()) {
130 sb.append(tr("deleted-on-server")).append(INDENT);
131 }
132 if (o.isReferrersDownloaded()) {
133 sb.append(tr("all-referrers-downloaded")).append(INDENT);
134 } else {
135 sb.append(tr("referrers-not-all-downloaded")).append(INDENT);
136 }
137 if (o.isModified()) {
138 sb.append(tr("modified")).append(INDENT);
139 }
140 if (o.isDisabledAndHidden()) {
141 sb.append(tr("filtered/hidden")).append(INDENT);
142 }
143 if (o.isDisabled()) {
144 sb.append(tr("filtered/disabled")).append(INDENT);
145 }
146 if (o.hasDirectionKeys()) {
147 if (o.reversedDirection()) {
148 sb.append(tr("has direction keys (reversed)")).append(INDENT);
149 } else {
150 sb.append(tr("has direction keys")).append(INDENT);
151 }
152 }
153 String state = sb.toString().trim();
154 if (!state.isEmpty()) {
155 add(tr("State: "), sb.toString().trim());
156 }
157 }
158
159 void addCommon(IPrimitive o) {
160 add(tr("Data Set: "), Integer.toHexString(o.getDataSet().hashCode()));
161 add(tr("Edited at: "), o.isTimestampEmpty() ? tr("<new object>")
162 : o.getInstant().toString());
163 add(tr("Edited by: "), o.getUser() == null ? tr("<new object>")
164 : getNameAndId(o.getUser().getName(), o.getUser().getId()));
165 add(tr("Version:"), " ", Integer.toString(o.getVersion()));
166 add(tr("In changeset: "), Integer.toString(o.getChangesetId()));
167 }
168
169 void addAttributes(IPrimitive o) {
170 if (o.hasKeys()) {
171 add(tr("Tags: "));
172 o.visitKeys((primitive, key, value) -> s.append(INDENT).append(INDENT).append(String.format("\"%s\"=\"%s\"%n", key, value)));
173 }
174 }
175
176 void addSpecial(IPrimitive o) {
177 if (o instanceof INode) {
178 addCoordinates((INode) o);
179 } else if (o instanceof IWay) {
180 addBbox(o);
181 final EastNorth centroid = Geometry.getCentroid(((IWay<?>) o).getNodes());
182 final String centroidMessage;
183 if (centroid == null) {
184 centroidMessage = tr("unknown");
185 } else {
186 centroidMessage = toStringCSV(false,
187 ProjectionRegistry.getProjection().eastNorth2latlon(centroid));
188 }
189 add(tr("Centroid: "), centroidMessage);
190 if (o instanceof Way) {
191 double length = ((Way) o).getLength();
192 String lenText = SystemOfMeasurement.getSystemOfMeasurement().getDistText(length);
193 add(tr("Length: {0}", lenText));
194
195 double avgNodeDistance = length / (((Way) o).getNodesCount() - 1);
196 String nodeDistText = SystemOfMeasurement.getSystemOfMeasurement().getDistText(avgNodeDistance);
197 add(tr("Average segment length: {0}", nodeDistText));
198
199 double stdDev = Utils.getStandardDeviation(((Way) o).getSegmentLengths(), avgNodeDistance);
200 String stdDevText = SystemOfMeasurement.getSystemOfMeasurement().getDistText(stdDev);
201 add(tr("Standard deviation: {0}", stdDevText));
202 }
203 if (o instanceof Way && ((Way) o).concernsArea() && ((Way) o).isClosed()) {
204 double area = Geometry.closedWayArea((Way) o);
205 String areaText = SystemOfMeasurement.getSystemOfMeasurement().getAreaText(area);
206 add(tr("Area: {0}", areaText));
207 }
208 addWayNodes((IWay<?>) o);
209 } else if (o instanceof IRelation) {
210 addBbox(o);
211 if (o instanceof Relation && ((Relation) o).concernsArea()) {
212 double area = Geometry.multipolygonArea(((Relation) o));
213 String areaText = SystemOfMeasurement.getSystemOfMeasurement().getAreaText(area);
214 add(tr("Area: {0}", areaText));
215 }
216 addRelationMembers((IRelation<?>) o);
217 }
218 }
219
220 void addRelationMembers(IRelation<?> r) {
221 add(trn("{0} Member: ", "{0} Members: ", r.getMembersCount(), r.getMembersCount()));
222 for (IRelationMember<?> m : r.getMembers()) {
223 s.append(INDENT).append(INDENT);
224 addHeadline(m.getMember());
225 s.append(tr(" as \"{0}\"", m.getRole()));
226 s.append(NL);
227 }
228 }
229
230 void addWayNodes(IWay<?> w) {
231 add(tr("{0} Nodes: ", w.getNodesCount()));
232 for (INode n : w.getNodes()) {
233 s.append(INDENT).append(INDENT);
234 addNameAndId(n);
235 s.append(NL);
236 }
237 }
238
239 void addBbox(IPrimitive o) {
240 BBox bbox = o.getBBox();
241 if (bbox != null) {
242 final LatLon bottomRight = bbox.getBottomRight();
243 final LatLon topLeft = bbox.getTopLeft();
244 add(tr("Bounding box: "), toStringCSV(false, bottomRight, topLeft));
245 add(tr("Bounding box (projected): "), toStringCSV(true, bottomRight, topLeft));
246 add(tr("Center of bounding box: "), toStringCSV(false, bbox.getCenter()));
247 }
248 }
249
250 void addCoordinates(INode n) {
251 if (n.isLatLonKnown()) {
252 add(tr("Coordinates:"), " ", toStringCSV(false, n));
253 add(tr("Coordinates (projected): "), toStringCSV(true, n));
254 Pair<Integer, Hemisphere> utmZone = TransverseMercator.locateUtmZone(n.getCoor());
255 String utmLabel = tr("UTM Zone");
256 add(utmLabel, utmLabel.endsWith(":") ? " " : ": ", Integer.toString(utmZone.a), utmZone.b.name().substring(0, 1));
257 }
258 }
259
260 void addReferrers(StringBuilder s, IPrimitive o) {
261 List<? extends IPrimitive> refs = o.getReferrers();
262 if (!refs.isEmpty()) {
263 add(tr("Part of: "));
264 for (IPrimitive p : refs) {
265 s.append(INDENT).append(INDENT);
266 addHeadline(p);
267 s.append(NL);
268 }
269 }
270 }
271
272 void addConflicts(OsmPrimitive o) {
273 Conflict<?> c = ((DataSet) ds).getConflicts().getConflictForMy(o);
274 if (c != null) {
275 add(tr("In conflict with: "));
276 addNameAndId(c.getTheir());
277 }
278 }
279
280 /**
281 * Returns the coordinates in human-readable format.
282 * @param projected whether to use projected coordinates
283 * @param coordinates the coordinates to format
284 * @return String in the format {@code "1.23456, 2.34567"}
285 */
286 private static String toStringCSV(boolean projected, ILatLon... coordinates) {
287 final AbstractCoordinateFormat format = projected
288 ? ProjectedCoordinateFormat.INSTANCE
289 : DecimalDegreesCoordinateFormat.INSTANCE;
290 return Arrays.stream(coordinates)
291 .flatMap(ll -> Stream.of(format.latToString(ll), format.lonToString(ll)))
292 .collect(Collectors.joining(", "));
293 }
294
295 @Override
296 public String toString() {
297 return s.toString();
298 }
299}
Note: See TracBrowser for help on using the repository browser.