source: josm/trunk/src/org/openstreetmap/josm/gui/dialogs/InspectPrimitiveDialog.java@ 3935

Last change on this file since 3935 was 3935, checked in by mjulius, 13 years ago

fix #6023 - ChangeSet ID in Hex

  • Property svn:eol-style set to native
File size: 12.1 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;
5
6import java.awt.Dimension;
7import java.awt.Font;
8import java.awt.GridBagLayout;
9import java.util.ArrayList;
10import java.util.Collection;
11import java.util.List;
12import java.util.Map.Entry;
13
14import javax.swing.JPanel;
15import javax.swing.JScrollPane;
16import javax.swing.JTabbedPane;
17import javax.swing.JTextArea;
18import javax.swing.SingleSelectionModel;
19import javax.swing.event.ChangeEvent;
20import javax.swing.event.ChangeListener;
21
22import org.openstreetmap.josm.Main;
23import org.openstreetmap.josm.data.osm.DataSet;
24import org.openstreetmap.josm.data.osm.Node;
25import org.openstreetmap.josm.data.osm.OsmPrimitive;
26import org.openstreetmap.josm.data.osm.Relation;
27import org.openstreetmap.josm.data.osm.RelationMember;
28import org.openstreetmap.josm.data.osm.User;
29import org.openstreetmap.josm.data.osm.Way;
30import org.openstreetmap.josm.gui.DefaultNameFormatter;
31import org.openstreetmap.josm.gui.ExtendedDialog;
32import org.openstreetmap.josm.gui.NavigatableComponent;
33import org.openstreetmap.josm.gui.mappaint.Cascade;
34import org.openstreetmap.josm.gui.mappaint.ElemStyle;
35import org.openstreetmap.josm.gui.mappaint.ElemStyles;
36import org.openstreetmap.josm.gui.mappaint.MapPaintStyles;
37import org.openstreetmap.josm.gui.mappaint.MultiCascade;
38import org.openstreetmap.josm.gui.mappaint.StyleCache;
39import org.openstreetmap.josm.gui.mappaint.StyleCache.StyleList;
40import org.openstreetmap.josm.gui.mappaint.StyleSource;
41import org.openstreetmap.josm.gui.mappaint.mapcss.MapCSSStyleSource;
42import org.openstreetmap.josm.gui.mappaint.xml.XmlStyleSource;
43import org.openstreetmap.josm.tools.DateUtils;
44import org.openstreetmap.josm.tools.GBC;
45import org.openstreetmap.josm.tools.SubclassFilteredCollection;
46
47/**
48 * Panel to inspect one or more OsmPrimitives.
49 *
50 * Gives an unfiltered view of the object's internal state.
51 * Might be useful for power users to give more detailed bug reports and
52 * to better understand the JOSM data representation.
53 *
54 * TODO: show conflicts
55 */
56public class InspectPrimitiveDialog extends ExtendedDialog {
57 protected Collection<OsmPrimitive> primitives;
58 private JTextArea txtData;
59 private JTextArea txtMappaint;
60 boolean mappaintTabLoaded;
61
62 public InspectPrimitiveDialog(Collection<OsmPrimitive> primitives) {
63 super(Main.parent, tr("Advanced object info"), new String[] {"Close"});
64 this.primitives = primitives;
65 setPreferredSize(new Dimension(750, 550));
66
67 setButtonIcons(new String[] {"ok.png"});
68 final JTabbedPane tabs = new JTabbedPane();
69 JPanel pData = buildDataPanel();
70 tabs.addTab(tr("data"), pData);
71 final JPanel pMapPaint = new JPanel();
72 tabs.addTab(tr("map style"), pMapPaint);
73 tabs.getModel().addChangeListener(new ChangeListener() {
74
75 @Override
76 public void stateChanged(ChangeEvent e) {
77 if (!mappaintTabLoaded && ((SingleSelectionModel) e.getSource()).getSelectedIndex() == 1) {
78 mappaintTabLoaded = true;
79 buildMapPaintPanel(pMapPaint);
80 createMapPaintText();
81 }
82 }
83 });
84 txtData.setText(buildDataText());
85 setContent(tabs, false);
86 }
87
88 protected JPanel buildDataPanel() {
89 JPanel p = new JPanel(new GridBagLayout());
90 txtData = new JTextArea();
91 txtData.setFont(new Font("Monospaced", txtData.getFont().getStyle(), txtData.getFont().getSize()));
92 txtData.setEditable(false);
93
94 JScrollPane scroll = new JScrollPane(txtData);
95
96 p.add(scroll, GBC.std().fill());
97 return p;
98 }
99
100 protected String buildDataText() {
101 StringBuilder s = new StringBuilder();
102 for (Node n : new SubclassFilteredCollection<OsmPrimitive, Node>(primitives, OsmPrimitive.nodePredicate)) {
103 s.append("Node id="+n.getUniqueId());
104 if (!checkDataSet(n)) {
105 s.append(" not in data set");
106 continue;
107 }
108 if (n.isIncomplete()) {
109 s.append(" incomplete\n");
110 addWayReferrer(s, n);
111 addRelationReferrer(s, n);
112 continue;
113 }
114 s.append(String.format(" lat=%s lon=%s (projected: x=%s, y=%s); ",
115 Double.toString(n.getCoor().lat()), Double.toString(n.getCoor().lon()),
116 Double.toString(n.getEastNorth().east()), Double.toString(n.getEastNorth().north())));
117 addCommon(s, n);
118 addAttributes(s, n);
119 addWayReferrer(s, n);
120 addRelationReferrer(s, n);
121 s.append('\n');
122 }
123
124 for (Way w : new SubclassFilteredCollection<OsmPrimitive, Way>(primitives, OsmPrimitive.wayPredicate)) {
125 s.append("Way id="+ w.getUniqueId());
126 if (!checkDataSet(w)) {
127 s.append(" not in data set");
128 continue;
129 }
130 if (w.isIncomplete()) {
131 s.append(" incomplete\n");
132 addRelationReferrer(s, w);
133 continue;
134 }
135 s.append(String.format(" %d nodes; ", w.getNodes().size()));
136 addCommon(s, w);
137 addAttributes(s, w);
138 addRelationReferrer(s, w);
139
140 s.append(" nodes:\n");
141 for (Node n : w.getNodes()) {
142 s.append(String.format(" %d\n", n.getUniqueId()));
143 }
144 s.append('\n');
145 }
146
147 for (Relation r : new SubclassFilteredCollection<OsmPrimitive, Relation>(primitives, OsmPrimitive.relationPredicate)) {
148 s.append("Relation id="+r.getUniqueId());
149 if (!checkDataSet(r)) {
150 s.append(" not in data set");
151 continue;
152 }
153 if (r.isIncomplete()) {
154 s.append(" incomplete\n");
155 addRelationReferrer(s, r);
156 continue;
157 }
158 s.append(String.format(" %d members; ",r.getMembersCount()));
159 addCommon(s, r);
160 addAttributes(s, r);
161 addRelationReferrer(s, r);
162
163 s.append(" members:\n");
164 for (RelationMember m : r.getMembers() ) {
165 s.append(String.format(" %s%d '%s'\n", m.getMember().getType().getAPIName().substring(0,1), m.getMember().getUniqueId(), m.getRole()));
166 }
167 s.append('\n');
168 }
169
170 return s.toString().trim();
171 }
172
173 protected void addCommon(StringBuilder s, OsmPrimitive o) {
174 s.append(String.format("Data set: %X; User: [%s]; ChangeSet id: %d; Timestamp: %s, Version: %d",
175 o.getDataSet().hashCode(),
176 userString(o.getUser()),
177 o.getChangesetId(),
178 DateUtils.fromDate(o.getTimestamp()),
179 o.getVersion()));
180
181 /* selected state is left out: not interesting as it is always selected */
182 if (o.isDeleted()) {
183 s.append("; deleted");
184 }
185 if (!o.isVisible()) {
186 s.append("; deleted-on-server");
187 }
188 if (o.isModified()) {
189 s.append("; modified");
190 }
191 if (o.isDisabledAndHidden()) {
192 s.append("; filtered/hidden");
193 }
194 if (o.isDisabled()) {
195 s.append("; filtered/disabled");
196 }
197 if (o.hasDirectionKeys()) {
198 s.append("; has direction keys");
199 if (o.reversedDirection()) {
200 s.append(" (reversed)");
201 }
202 }
203 s.append("\n");
204 }
205
206 protected void addAttributes(StringBuilder s, OsmPrimitive o) {
207 if (o.hasKeys()) {
208 s.append(" tags:\n");
209 for (String key: o.keySet()) {
210 s.append(String.format(" \"%s\"=\"%s\"\n", key, o.get(key)));
211 }
212 }
213 }
214
215 protected void addWayReferrer(StringBuilder s, Node n) {
216 // add way referrer
217 List<OsmPrimitive> refs = n.getReferrers();
218 Collection<Way> wayRefs = new SubclassFilteredCollection<OsmPrimitive, Way>(refs, OsmPrimitive.wayPredicate);
219 if (wayRefs.size() > 0) {
220 s.append(" way referrer:\n");
221 for (Way w : wayRefs) {
222 s.append(" "+w.getUniqueId()+"\n");
223 }
224 }
225 }
226
227 protected void addRelationReferrer(StringBuilder s, OsmPrimitive o) {
228 List<OsmPrimitive> refs = o.getReferrers();
229 Collection<Relation> relRefs = new SubclassFilteredCollection<OsmPrimitive, Relation>(refs, OsmPrimitive.relationPredicate);
230 if (relRefs.size() > 0) {
231 s.append(" relation referrer:\n");
232 for (Relation r : relRefs) {
233 s.append(" "+r.getUniqueId()+"\n");
234 }
235 }
236 }
237
238 /**
239 * See if primitive is in a data set properly.
240 * This does not hold for primitives that are new and deleted.
241 */
242 protected boolean checkDataSet(OsmPrimitive o) {
243 DataSet ds = o.getDataSet();
244 if (ds == null)
245 return false;
246 return ds.getPrimitiveById(o) != null;
247 }
248
249 protected String userString(User user) {
250 if (user == null)
251 return "<null>";
252
253 List<String> names = user.getNames();
254
255 StringBuilder us = new StringBuilder();
256
257 us.append("id:"+user.getId());
258 if (names.size() == 1) {
259 us.append(" name:"+user.getName());
260 }
261 else if (names.size() > 1) {
262 us.append(String.format(" %d names:%s", names.size(), user.getName()));
263 }
264 return us.toString();
265 }
266
267 protected void buildMapPaintPanel(JPanel p) {
268 p.setLayout(new GridBagLayout());
269 txtMappaint = new JTextArea();
270 txtMappaint.setFont(new Font("Monospaced", txtMappaint.getFont().getStyle(), txtMappaint.getFont().getSize()));
271 txtMappaint.setEditable(false);
272
273 p.add(new JScrollPane(txtMappaint), GBC.std().fill());
274 }
275
276 protected void createMapPaintText() {
277 final Collection<OsmPrimitive> sel = Main.main.getCurrentDataSet().getSelected();
278 ElemStyles elemstyles = MapPaintStyles.getStyles();
279 NavigatableComponent nc = Main.map.mapView;
280 double scale = nc.getDist100Pixel();
281
282 for (OsmPrimitive osm : sel) {
283 txtMappaint.append("Styles Cache for \""+osm.getDisplayName(DefaultNameFormatter.getInstance())+"\":");
284
285 MultiCascade mc = new MultiCascade();
286
287 for (StyleSource s : elemstyles.getStyleSources()) {
288 if (s.active) {
289 txtMappaint.append("\n\n> applying "+getSort(s)+" style \""+s.getDisplayString()+"\n");
290 s.apply(mc, osm, scale, null, false);
291 txtMappaint.append("\nRange:"+mc.range);
292 for (Entry<String, Cascade> e : mc.getLayers()) {
293 txtMappaint.append("\n "+e.getKey()+": \n"+e.getValue());
294 }
295 } else {
296 txtMappaint.append("\n\n> skipping \""+s.getDisplayString()+"\" (not active)");
297 }
298 }
299 txtMappaint.append("\n\nList of generated Styles:\n");
300 StyleList sl = elemstyles.get(osm, scale, nc);
301 for (ElemStyle s : sl) {
302 txtMappaint.append(" * "+s+"\n");
303 }
304 txtMappaint.append("\n\n");
305 }
306
307 if (sel.size() == 2) {
308 List<OsmPrimitive> selList = new ArrayList<OsmPrimitive>(sel);
309 StyleCache sc1 = selList.get(0).mappaintStyle;
310 StyleCache sc2 = selList.get(1).mappaintStyle;
311 if (sc1 == sc2) {
312 txtMappaint.append("The 2 selected Objects have identical style caches.");
313 }
314 if (!sc1.equals(sc2)) {
315 txtMappaint.append("The 2 selected Objects have different style caches.");
316 }
317 if (sc1.equals(sc2) && sc1 != sc2) {
318 txtMappaint.append("Warning: The 2 selected Objects have equal, but not identical style caches.");
319 }
320 }
321 }
322
323 private String getSort(StyleSource s) {
324 if (s instanceof XmlStyleSource)
325 return "xml";
326 if (s instanceof MapCSSStyleSource)
327 return "mapcss";
328 return "unkown";
329 }
330
331}
Note: See TracBrowser for help on using the repository browser.