Changeset 18365 in josm
- Timestamp:
- 2022-01-25T19:05:27+01:00 (20 months ago)
- Location:
- trunk
- Files:
-
- 4 added
- 9 edited
Legend:
- Unmodified
- Added
- Removed
-
trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagChecker.java
r18275 r18365 255 255 256 256 /** 257 * A handler for assertion error messages (for not fulfilled "assertMatch", "assertNoMatch").258 */259 @FunctionalInterface260 interface AssertionConsumer extends Consumer<String> {261 }262 263 /**264 257 * Adds a new MapCSS config file from the given URL. 265 258 * @param url The unique URL of the MapCSS config file … … 275 268 } 276 269 277 synchronized ParseResult addMapCSS(String url, AssertionConsumer assertionConsumer) throws ParseException, IOException { 270 /** 271 * Adds a new MapCSS config file from the given URL. <br /> 272 * NOTE: You should prefer {@link #addMapCSS(String)} unless you <i>need</i> to know what the assertions return. 273 * 274 * @param url The unique URL of the MapCSS config file 275 * @param assertionConsumer A string consumer for error messages. 276 * @return List of tag checks and parsing errors, or null 277 * @throws ParseException if the config file does not match MapCSS syntax 278 * @throws IOException if any I/O error occurs 279 * @since 18365 (public, primarily for ValidatorCLI) 280 */ 281 public synchronized ParseResult addMapCSS(String url, Consumer<String> assertionConsumer) throws ParseException, IOException { 278 282 CheckParameterUtil.ensureParameterNotNull(url, "url"); 279 283 ParseResult result; -
trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerAsserts.java
r17981 r18365 11 11 import java.util.Optional; 12 12 import java.util.Set; 13 import java.util.function.Consumer; 13 14 import java.util.stream.Collectors; 14 15 … … 47 48 */ 48 49 static void checkAsserts(final MapCSSTagCheckerRule check, final Map<String, Boolean> assertions, 49 final MapCSSTagChecker.AssertionConsumerassertionConsumer) {50 final Consumer<String> assertionConsumer) { 50 51 final DataSet ds = new DataSet(); 51 52 Logging.debug("Check: {0}", check); -
trunk/src/org/openstreetmap/josm/data/validation/tests/MapCSSTagCheckerRule.java
r18193 r18365 16 16 import java.util.Optional; 17 17 import java.util.Set; 18 import java.util.function.Consumer; 18 19 import java.util.function.Predicate; 19 20 import java.util.regex.Matcher; … … 32 33 import org.openstreetmap.josm.data.validation.Test; 33 34 import org.openstreetmap.josm.data.validation.TestError; 34 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker.AssertionConsumer;35 35 import org.openstreetmap.josm.gui.mappaint.Environment; 36 36 import org.openstreetmap.josm.gui.mappaint.Keyword; … … 107 107 private static final String POSSIBLE_THROWS = "throwError/throwWarning/throwOther"; 108 108 109 static MapCSSTagCheckerRule ofMapCSSRule(final MapCSSRule rule, AssertionConsumerassertionConsumer) throws IllegalDataException {109 static MapCSSTagCheckerRule ofMapCSSRule(final MapCSSRule rule, Consumer<String> assertionConsumer) throws IllegalDataException { 110 110 final MapCSSTagCheckerRule check = new MapCSSTagCheckerRule(rule); 111 111 final Map<String, Boolean> assertions = new HashMap<>(); … … 186 186 } 187 187 188 static MapCSSTagChecker.ParseResult readMapCSS(Reader css, AssertionConsumerassertionConsumer) throws ParseException {188 static MapCSSTagChecker.ParseResult readMapCSS(Reader css, Consumer<String> assertionConsumer) throws ParseException { 189 189 CheckParameterUtil.ensureParameterNotNull(css, "css"); 190 190 -
trunk/src/org/openstreetmap/josm/gui/MainApplication.java
r18361 r18365 99 99 import org.openstreetmap.josm.data.projection.datum.NTV2GridShiftFileWrapper; 100 100 import org.openstreetmap.josm.data.projection.datum.NTV2Proj4DirGridShiftFileSource; 101 import org.openstreetmap.josm.data.validation.ValidatorCLI; 101 102 import org.openstreetmap.josm.data.validation.tests.MapCSSTagChecker; 102 103 import org.openstreetmap.josm.gui.ProgramArguments.Option; … … 312 313 registerCLIModule(ProjectionCLI.INSTANCE); 313 314 registerCLIModule(RenderingCLI.INSTANCE); 315 registerCLIModule(ValidatorCLI.INSTANCE); 314 316 } 315 317 … … 661 663 "\trunjosm "+tr("launch JOSM (default, performed when no command is specified)")+'\n'+ 662 664 "\trender "+tr("render data and save the result to an image file")+'\n'+ 663 "\tproject "+tr("convert coordinates from one coordinate reference system to another")+"\n\n"+ 665 "\tproject " + tr("convert coordinates from one coordinate reference system to another")+ '\n' + 666 "\tvalidate " + tr("validate data") + "\n\n" + 664 667 tr("For details on the {0} and {1} commands, run them with the {2} option.", "render", "project", "--help")+'\n'+ 665 668 tr("The remainder of this help page documents the {0} command.", "runjosm")+"\n\n"+ -
trunk/src/org/openstreetmap/josm/gui/io/importexport/OsmImporter.java
r14717 r18365 98 98 associatedFile == null ? OsmDataLayer.createNewName() : associatedFile.getName(), pm); 99 99 100 final OsmDataLayer layer = data.getLayer(); 101 // Note: addLayer calls GuiHelper.runInEDTAndWaitWithException 102 MainApplication.getLayerManager().addLayer(layer); 100 103 // FIXME: remove UI stuff from IO subsystem 101 104 GuiHelper.runInEDT(() -> { 102 OsmDataLayer layer = data.getLayer();103 MainApplication.getLayerManager().addLayer(layer);104 105 data.getPostLayerTask().run(); 105 106 data.getLayer().onPostLoadFromFile(); -
trunk/src/org/openstreetmap/josm/gui/preferences/projection/CustomProjectionChoice.java
r18211 r18365 24 24 import org.openstreetmap.josm.data.projection.Projections; 25 25 import org.openstreetmap.josm.gui.ExtendedDialog; 26 import org.openstreetmap.josm.gui.tagging.ac.AutoCompTextField; 26 27 import org.openstreetmap.josm.gui.widgets.AbstractTextComponentValidator; 27 28 import org.openstreetmap.josm.gui.widgets.HistoryComboBox; … … 53 54 private static class PreferencePanel extends JPanel { 54 55 55 public JosmTextFieldinput;56 public AutoCompTextField<String> input; 56 57 private HistoryComboBox cbInput; 57 58 … … 61 62 62 63 private void build(String initialText, final ActionListener listener) { 63 input = new JosmTextField(30);64 input = new AutoCompTextField<>(30); 64 65 cbInput = new HistoryComboBox(); 65 66 cbInput.setEditor(new BasicComboBoxEditor() { -
trunk/src/org/openstreetmap/josm/gui/progress/AbstractProgressMonitor.java
r12675 r18365 234 234 ==================*/ 235 235 236 /** 237 * Update progress message 238 * @param value The percentage of completion (this and child progress) 239 */ 236 240 protected abstract void updateProgress(double value); 237 241 -
trunk/src/org/openstreetmap/josm/gui/util/GuiHelper.java
r17090 r18365 289 289 */ 290 290 public static void assertCallFromEdt() { 291 if (!SwingUtilities.isEventDispatchThread() ) {291 if (!SwingUtilities.isEventDispatchThread() && !GraphicsEnvironment.isHeadless()) { 292 292 throw new IllegalStateException( 293 293 "Needs to be called from the EDT thread, not from " + Thread.currentThread().getName()); -
trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java
r17570 r18365 7 7 import java.math.BigDecimal; 8 8 import java.math.RoundingMode; 9 import java.time.Instant; 10 import java.util.ArrayList; 11 import java.util.Arrays; 9 12 import java.util.Collection; 10 13 import java.util.Collections; 14 import java.util.EnumSet; 11 15 import java.util.HashSet; 12 16 import java.util.Iterator; 13 17 import java.util.List; 14 18 import java.util.Map; 15 import java.util.Map.Entry;16 19 import java.util.Set; 20 import java.util.stream.Collectors; 17 21 import java.util.stream.Stream; 18 22 … … 31 35 import org.openstreetmap.josm.data.coor.LatLon; 32 36 import org.openstreetmap.josm.data.osm.DataSet; 37 import org.openstreetmap.josm.data.osm.INode; 38 import org.openstreetmap.josm.data.osm.IWay; 33 39 import org.openstreetmap.josm.data.osm.MultipolygonBuilder; 34 import org.openstreetmap.josm.data.osm.MultipolygonBuilder.JoinedPolygon;35 40 import org.openstreetmap.josm.data.osm.Node; 36 41 import org.openstreetmap.josm.data.osm.OsmPrimitive; 37 42 import org.openstreetmap.josm.data.osm.Relation; 43 import org.openstreetmap.josm.data.osm.RelationMember; 38 44 import org.openstreetmap.josm.data.osm.Way; 39 45 import org.openstreetmap.josm.data.osm.visitor.OsmPrimitiveVisitor; … … 42 48 import org.openstreetmap.josm.data.projection.Projections; 43 49 import org.openstreetmap.josm.gui.mappaint.ElemStyles; 50 import org.openstreetmap.josm.tools.Geometry; 44 51 import org.openstreetmap.josm.tools.Logging; 45 52 import org.openstreetmap.josm.tools.Pair; 53 import org.openstreetmap.josm.tools.Utils; 46 54 47 55 /** … … 52 60 public class GeoJSONWriter { 53 61 62 enum Options { 63 /** If using the right hand rule, we have to ensure that the "right" side is the interior of the object. */ 64 RIGHT_HAND_RULE, 65 /** Write OSM information to the feature properties field. This tries to follow the Overpass turbo format. */ 66 WRITE_OSM_INFORMATION, 67 /** Skip empty nodes */ 68 SKIP_EMPTY_NODES 69 } 70 54 71 private final DataSet data; 55 private final Projection projection;72 private static final Projection projection = Projections.getProjectionByCode("EPSG:4326"); // WGS 84 56 73 private static final BooleanProperty SKIP_EMPTY_NODES = new BooleanProperty("geojson.export.skip-empty-nodes", true); 57 74 private static final BooleanProperty UNTAGGED_CLOSED_IS_POLYGON = new BooleanProperty("geojson.export.untagged-closed-is-polygon", false); 58 75 private static final Set<Way> processedMultipolygonWays = new HashSet<>(); 76 private EnumSet<Options> options = EnumSet.noneOf(Options.class); 59 77 60 78 /** … … 78 96 public GeoJSONWriter(DataSet ds) { 79 97 this.data = ds; 80 this.projection = Projections.getProjectionByCode("EPSG:4326"); // WGS 84 98 if (Boolean.TRUE.equals(SKIP_EMPTY_NODES.get())) { 99 this.options.add(Options.SKIP_EMPTY_NODES); 100 } 101 } 102 103 /** 104 * Set the options for this writer. See {@link Options}. 105 * @param options The options to set. 106 */ 107 void setOptions(final Options... options) { 108 this.options.clear(); 109 this.options.addAll(Arrays.asList(options)); 81 110 } 82 111 … … 118 147 } 119 148 149 /** 150 * Convert a primitive to a json object 151 */ 120 152 private class GeometryPrimitiveVisitor implements OsmPrimitiveVisitor { 121 153 … … 142 174 return; 143 175 } 144 final JsonArrayBuilder array = getCoorsArray(w.getNodes());145 176 boolean writeAsPolygon = w.isClosed() && ((!w.isTagged() && UNTAGGED_CLOSED_IS_POLYGON.get()) 146 177 || ElemStyles.hasAreaElemStyle(w, false)); 178 final List<Node> nodes = w.getNodes(); 179 if (writeAsPolygon && options.contains(Options.RIGHT_HAND_RULE) && Geometry.isClockwise(nodes)) { 180 Collections.reverse(nodes); 181 } 182 final JsonArrayBuilder array = getCoorsArray(nodes); 147 183 if (writeAsPolygon) { 148 184 geomObj.add("type", "Polygon"); … … 160 196 return; 161 197 } 162 try { 163 final Pair<List<JoinedPolygon>, List<JoinedPolygon>> mp = MultipolygonBuilder.joinWays(r); 198 if (r.isMultipolygon()) { 199 try { 200 this.visitMultipolygon(r); 201 return; 202 } catch (MultipolygonBuilder.JoinedPolygonCreationException ex) { 203 Logging.warn("GeoJSON: Failed to export multipolygon {0}, falling back to other multi geometry types", r.getUniqueId()); 204 Logging.warn(ex); 205 } 206 } 207 // These are run if (a) r is not a multipolygon or (b) r is not a well-formed multipolygon. 208 if (r.getMemberPrimitives().stream().allMatch(IWay.class::isInstance)) { 209 this.visitMultiLineString(r); 210 } else if (r.getMemberPrimitives().stream().allMatch(INode.class::isInstance)) { 211 this.visitMultiPoints(r); 212 } else { 213 this.visitMultiGeometry(r); 214 } 215 } 216 217 /** 218 * Visit a multi-part geometry. 219 * Note: Does not currently recurse down relations. RFC 7946 indicates that we 220 * should avoid nested geometry collections. This behavior may change any time in the future! 221 * @param r The relation to visit. 222 */ 223 private void visitMultiGeometry(final Relation r) { 224 final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder(); 225 r.getMemberPrimitives().stream().filter(p -> !(p instanceof Relation)) 226 .map(p -> { 227 final JsonObjectBuilder tempGeomObj = Json.createObjectBuilder(); 228 p.accept(new GeometryPrimitiveVisitor(tempGeomObj)); 229 return tempGeomObj.build(); 230 }).forEach(jsonArrayBuilder::add); 231 geomObj.add("type", "GeometryCollection"); 232 geomObj.add("geometries", jsonArrayBuilder); 233 } 234 235 /** 236 * Visit a relation that only contains points 237 * @param r The relation to visit 238 */ 239 private void visitMultiPoints(final Relation r) { 240 final JsonArrayBuilder multiPoint = Json.createArrayBuilder(); 241 r.getMembers().stream().map(RelationMember::getMember).filter(Node.class::isInstance).map(Node.class::cast) 242 .map(Node::getCoor).map(latLon -> getCoorArray(null, latLon)) 243 .forEach(multiPoint::add); 244 geomObj.add("type", "MultiPoint"); 245 geomObj.add("coordinates", multiPoint); 246 } 247 248 /** 249 * Visit a relation that is a multi line string 250 * @param r The relation to convert 251 */ 252 private void visitMultiLineString(final Relation r) { 253 final JsonArrayBuilder multiLine = Json.createArrayBuilder(); 254 r.getMembers().stream().map(RelationMember::getMember).filter(Way.class::isInstance).map(Way.class::cast) 255 .map(Way::getNodes).map(p -> { 256 JsonArrayBuilder array = getCoorsArray(p); 257 LatLon ll = p.get(0).getCoor(); 258 // since first node is not duplicated as last node 259 return ll != null ? array.add(getCoorArray(null, ll)) : array; 260 }).forEach(multiLine::add); 261 geomObj.add("type", "MultiLineString"); 262 geomObj.add("coordinates", multiLine); 263 processedMultipolygonWays.addAll(r.getMemberPrimitives(Way.class)); 264 } 265 266 /** 267 * Convert a multipolygon to geojson 268 * @param r The relation to convert 269 * @throws MultipolygonBuilder.JoinedPolygonCreationException See {@link MultipolygonBuilder#joinWays(Relation)}. 270 * Note that if the exception is thrown, {@link #geomObj} will not have been modified. 271 */ 272 private void visitMultipolygon(final Relation r) throws MultipolygonBuilder.JoinedPolygonCreationException { 273 final Pair<List<MultipolygonBuilder.JoinedPolygon>, List<MultipolygonBuilder.JoinedPolygon>> mp = 274 MultipolygonBuilder.joinWays(r); 164 275 final JsonArrayBuilder polygon = Json.createArrayBuilder(); 165 Stream.concat(mp.a.stream(), mp.b.stream()) 276 // Peek would theoretically be better for these two streams, but SonarLint doesn't like it. 277 // java:S3864: "Stream.peek" should be used with caution 278 final Stream<List<Node>> outer = mp.a.stream().map(MultipolygonBuilder.JoinedPolygon::getNodes).map(nodes -> { 279 final ArrayList<Node> tempNodes = new ArrayList<>(nodes); 280 tempNodes.add(tempNodes.get(0)); 281 if (options.contains(Options.RIGHT_HAND_RULE) && Geometry.isClockwise(tempNodes)) { 282 Collections.reverse(nodes); 283 } 284 return nodes; 285 }); 286 final Stream<List<Node>> inner = mp.b.stream().map(MultipolygonBuilder.JoinedPolygon::getNodes).map(nodes -> { 287 final ArrayList<Node> tempNodes = new ArrayList<>(nodes); 288 tempNodes.add(tempNodes.get(0)); 289 // Note that we are checking !Geometry.isClockwise, which is different from the outer 290 // ring check. 291 if (options.contains(Options.RIGHT_HAND_RULE) && !Geometry.isClockwise(tempNodes)) { 292 Collections.reverse(nodes); 293 } 294 return nodes; 295 }); 296 Stream.concat(outer, inner) 166 297 .map(p -> { 167 JsonArrayBuilder array = getCoorsArray(p .getNodes());168 LatLon ll = p.get Nodes().get(0).getCoor();298 JsonArrayBuilder array = getCoorsArray(p); 299 LatLon ll = p.get(0).getCoor(); 169 300 // since first node is not duplicated as last node 170 301 return ll != null ? array.add(getCoorArray(null, ll)) : array; 171 302 }) 172 303 .forEach(polygon::add); 304 final JsonArrayBuilder multiPolygon = Json.createArrayBuilder().add(polygon); 173 305 geomObj.add("type", "MultiPolygon"); 174 final JsonArrayBuilder multiPolygon = Json.createArrayBuilder().add(polygon);175 306 geomObj.add("coordinates", multiPolygon); 176 307 processedMultipolygonWays.addAll(r.getMemberPrimitives(Way.class)); 177 } catch (MultipolygonBuilder.JoinedPolygonCreationException ex) {178 Logging.warn("GeoJSON: Failed to export multipolygon {0}", r.getUniqueId());179 Logging.warn(ex);180 }181 308 } 182 309 … … 205 332 protected void appendPrimitive(OsmPrimitive p, JsonArrayBuilder array) { 206 333 if (p.isIncomplete() || 207 ( SKIP_EMPTY_NODES.get() && p instanceof Node && p.getKeys().isEmpty())) {334 (this.options.contains(Options.SKIP_EMPTY_NODES) && p instanceof Node && p.getKeys().isEmpty())) { 208 335 return; 209 336 } … … 211 338 // Properties 212 339 final JsonObjectBuilder propObj = Json.createObjectBuilder(); 213 for (Entry<String, String> t : p.getKeys().entrySet()) { 214 propObj.add(t.getKey(), convertValueToJson(t.getValue())); 340 for (Map.Entry<String, String> t : p.getKeys().entrySet()) { 341 // If writing OSM information, follow Overpass syntax (escape `@` with another `@`) 342 final String key = options.contains(Options.WRITE_OSM_INFORMATION) && t.getKey().startsWith("@") 343 ? '@' + t.getKey() : t.getKey(); 344 propObj.add(key, convertValueToJson(t.getValue())); 345 } 346 if (options.contains(Options.WRITE_OSM_INFORMATION)) { 347 // Use the same format as Overpass 348 propObj.add("@id", p.getPrimitiveId().getType().getAPIName() + '/' + p.getUniqueId()); // type/id 349 if (!p.isNew()) { 350 propObj.add("@timestamp", Instant.ofEpochSecond(p.getRawTimestamp()).toString()); 351 propObj.add("@version", Integer.toString(p.getVersion())); 352 propObj.add("@changeset", Long.toString(p.getChangesetId())); 353 } 354 if (p.getUser() != null) { 355 propObj.add("@user", p.getUser().getName()); 356 propObj.add("@uid", p.getUser().getId()); 357 } 358 if (options.contains(Options.WRITE_OSM_INFORMATION) && p.getReferrers(true).stream().anyMatch(Relation.class::isInstance)) { 359 final JsonArrayBuilder jsonArrayBuilder = Json.createArrayBuilder(); 360 for (Relation relation : Utils.filteredCollection(p.getReferrers(), Relation.class)) { 361 final JsonObjectBuilder relationObject = Json.createObjectBuilder(); 362 relationObject.add("rel", relation.getId()); 363 Collection<RelationMember> members = relation.getMembersFor(Collections.singleton(p)); 364 // Each role is a separate object in overpass-turbo geojson export. For now, just concat them. 365 relationObject.add("role", 366 members.stream().map(RelationMember::getRole).collect(Collectors.joining(";"))); 367 final JsonObjectBuilder relationKeys = Json.createObjectBuilder(); 368 // Uncertain if the @relation reltags need to be @ escaped. I don't think so, as example output 369 // didn't have any metadata in it. 370 for (Map.Entry<String, String> tag : relation.getKeys().entrySet()) { 371 relationKeys.add(tag.getKey(), convertValueToJson(tag.getValue())); 372 } 373 relationObject.add("reltags", relationKeys); 374 } 375 propObj.add("@relations", jsonArrayBuilder); 376 } 215 377 } 216 378 final JsonObject prop = propObj.build();
Note: See TracChangeset
for help on using the changeset viewer.