Changeset 10817 in josm


Ignore:
Timestamp:
2016-08-15T21:30:34+02:00 (8 years ago)
Author:
simon04
Message:

see #7307 - Enhance GeoJSON export

  • Save ways as LineString or Polygon depending on the area style.
  • Save multipolygons as MultiPolygon.
Location:
trunk
Files:
4 edited

Legend:

Unmodified
Added
Removed
  • trunk/src/org/openstreetmap/josm/data/osm/MultipolygonBuilder.java

    r10632 r10817  
    1414import java.util.HashSet;
    1515import java.util.List;
     16import java.util.Map;
    1617import java.util.Set;
    1718import java.util.concurrent.ForkJoinPool;
    1819import java.util.concurrent.ForkJoinTask;
    1920import java.util.concurrent.RecursiveTask;
     21import java.util.stream.Collectors;
    2022
    2123import org.openstreetmap.josm.Main;
     24import org.openstreetmap.josm.tools.CheckParameterUtil;
    2225import org.openstreetmap.josm.tools.Geometry;
    2326import org.openstreetmap.josm.tools.Geometry.PolygonIntersection;
     
    165168
    166169    /**
     170     * Joins the given {@code multipolygon} to a pair of outer and inner multipolygon rings.
     171     *
     172     * @param multipolygon the multipolygon to join.
     173     * @return a pair of outer and inner multipolygon rings.
     174     * @throws JoinedPolygonCreationException if the creation fails.
     175     */
     176    public static Pair<List<JoinedPolygon>, List<JoinedPolygon>> joinWays(Relation multipolygon) throws JoinedPolygonCreationException {
     177        CheckParameterUtil.ensureThat(multipolygon.isMultipolygon(), "multipolygon.isMultipolygon");
     178        final Map<String, Set<Way>> members = multipolygon.getMembers().stream()
     179                .filter(RelationMember::isWay)
     180                .collect(Collectors.groupingBy(RelationMember::getRole, Collectors.mapping(RelationMember::getWay, Collectors.toSet())));
     181        final List<JoinedPolygon> outerRings = joinWays(members.getOrDefault("outer", Collections.emptySet()));
     182        final List<JoinedPolygon> innerRings = joinWays(members.getOrDefault("inner", Collections.emptySet()));
     183        return Pair.create(outerRings, innerRings);
     184    }
     185
     186    /**
    167187     * Joins the given {@code ways} to multipolygon rings.
    168188     * @param ways the ways to join.
  • trunk/src/org/openstreetmap/josm/io/GeoJSONWriter.java

    r10173 r10817  
    77import java.util.HashMap;
    88import java.util.Iterator;
     9import java.util.List;
    910import java.util.Map;
    1011import java.util.Map.Entry;
     12import java.util.stream.Stream;
    1113
    1214import javax.json.Json;
     
    2022import org.openstreetmap.josm.data.coor.LatLon;
    2123import org.openstreetmap.josm.data.osm.DataSet;
    22 import org.openstreetmap.josm.data.osm.INode;
    23 import org.openstreetmap.josm.data.osm.IRelation;
    24 import org.openstreetmap.josm.data.osm.IWay;
     24import org.openstreetmap.josm.data.osm.MultipolygonBuilder;
     25import org.openstreetmap.josm.data.osm.MultipolygonBuilder.JoinedPolygon;
    2526import org.openstreetmap.josm.data.osm.Node;
    2627import org.openstreetmap.josm.data.osm.OsmPrimitive;
     28import org.openstreetmap.josm.data.osm.Relation;
    2729import org.openstreetmap.josm.data.osm.Way;
    28 import org.openstreetmap.josm.data.osm.visitor.PrimitiveVisitor;
     30import org.openstreetmap.josm.data.osm.visitor.AbstractVisitor;
    2931import org.openstreetmap.josm.data.projection.Projection;
    3032import org.openstreetmap.josm.gui.layer.OsmDataLayer;
     33import org.openstreetmap.josm.gui.mappaint.ElemStyles;
     34import org.openstreetmap.josm.tools.Pair;
    3135
    3236/**
     
    8084    }
    8185
    82     private class GeometryPrimitiveVisitor implements PrimitiveVisitor {
     86    private class GeometryPrimitiveVisitor extends AbstractVisitor {
    8387
    8488        private final JsonObjectBuilder geomObj;
     
    8993
    9094        @Override
    91         public void visit(INode n) {
     95        public void visit(Node n) {
    9296            geomObj.add("type", "Point");
    9397            LatLon ll = n.getCoor();
    9498            if (ll != null) {
    95                 geomObj.add("coordinates", getCoorArray(Json.createArrayBuilder(), n.getCoor()));
     99                geomObj.add("coordinates", getCoorArray(null, n.getCoor()));
    96100            }
    97101        }
    98102
    99103        @Override
    100         public void visit(IWay w) {
    101             geomObj.add("type", "LineString");
    102             if (w instanceof Way) {
    103                 JsonArrayBuilder array = Json.createArrayBuilder();
    104                 for (Node n : ((Way) w).getNodes()) {
    105                     LatLon ll = n.getCoor();
    106                     if (ll != null) {
    107                         array.add(getCoorArray(Json.createArrayBuilder(), ll));
    108                     }
     104        public void visit(Way w) {
     105            if (w != null) {
     106                final JsonArrayBuilder array = getCoorsArray(w.getNodes());
     107                if (ElemStyles.hasAreaElemStyle(w, false)) {
     108                    final JsonArrayBuilder container = Json.createArrayBuilder().add(array);
     109                    geomObj.add("type", "Polygon");
     110                    geomObj.add("coordinates", container);
     111                } else {
     112                    geomObj.add("type", "LineString");
     113                    geomObj.add("coordinates", array);
    109114                }
    110                 geomObj.add("coordinates", array);
    111115            }
    112116        }
    113117
    114118        @Override
    115         public void visit(IRelation r) {
    116             // Do nothing
     119        public void visit(Relation r) {
     120            if (r != null && r.isMultipolygon() && !r.hasIncompleteMembers()) {
     121                final Pair<List<JoinedPolygon>, List<JoinedPolygon>> mp = MultipolygonBuilder.joinWays(r);
     122                final JsonArrayBuilder polygon = Json.createArrayBuilder();
     123                Stream.concat(mp.a.stream(), mp.b.stream())
     124                        .map(p -> getCoorsArray(p.getNodes())
     125                                // since first node is not duplicated as last node
     126                                .add(getCoorArray(null, p.getNodes().get(0).getCoor())))
     127                        .forEach(polygon::add);
     128                geomObj.add("type", "MultiPolygon");
     129                final JsonArrayBuilder multiPolygon = Json.createArrayBuilder().add(polygon);
     130                geomObj.add("coordinates", multiPolygon);
     131            }
    117132        }
    118133    }
     
    123138
    124139    private static JsonArrayBuilder getCoorArray(JsonArrayBuilder builder, EastNorth c) {
    125         return builder
     140        return builder != null ? builder : Json.createArrayBuilder()
    126141                .add(BigDecimal.valueOf(c.getX()).setScale(11, RoundingMode.HALF_UP))
    127142                .add(BigDecimal.valueOf(c.getY()).setScale(11, RoundingMode.HALF_UP));
     143    }
     144
     145    private JsonArrayBuilder getCoorsArray(Iterable<Node> nodes) {
     146        final JsonArrayBuilder builder = Json.createArrayBuilder();
     147        for (Node n : nodes) {
     148            LatLon ll = n.getCoor();
     149            if (ll != null) {
     150                builder.add(getCoorArray(null, ll));
     151            }
     152        }
     153        return builder;
    128154    }
    129155
     
    177203        JsonArrayBuilder array = Json.createArrayBuilder();
    178204        if (ds != null) {
    179             for (Node n : ds.getNodes()) {
    180                 appendPrimitive(n, array);
    181             }
    182             for (Way w : ds.getWays()) {
    183                 appendPrimitive(w, array);
    184             }
     205            ds.allPrimitives().forEach(p -> appendPrimitive(p, array));
    185206        }
    186207        object.add("features", array);
  • trunk/src/org/openstreetmap/josm/tools/Geometry.java

    r10797 r10817  
    1212import java.util.Comparator;
    1313import java.util.EnumSet;
    14 import java.util.HashSet;
    1514import java.util.LinkedHashSet;
    1615import java.util.List;
     
    2625import org.openstreetmap.josm.data.osm.BBox;
    2726import org.openstreetmap.josm.data.osm.MultipolygonBuilder;
     27import org.openstreetmap.josm.data.osm.MultipolygonBuilder.JoinedPolygon;
    2828import org.openstreetmap.josm.data.osm.Node;
    2929import org.openstreetmap.josm.data.osm.NodePositionComparator;
    3030import org.openstreetmap.josm.data.osm.OsmPrimitive;
    31 import org.openstreetmap.josm.data.osm.OsmPrimitiveType;
    3231import org.openstreetmap.josm.data.osm.Relation;
    33 import org.openstreetmap.josm.data.osm.RelationMember;
    3432import org.openstreetmap.josm.data.osm.Way;
    3533import org.openstreetmap.josm.data.osm.visitor.paint.relations.Multipolygon;
     
    863861    }
    864862
    865     public static class MultiPolygonMembers {
    866         public final Set<Way> outers = new HashSet<>();
    867         public final Set<Way> inners = new HashSet<>();
    868 
    869         public MultiPolygonMembers(Relation multiPolygon) {
    870             for (RelationMember m : multiPolygon.getMembers()) {
    871                 if (m.getType().equals(OsmPrimitiveType.WAY)) {
    872                     if ("outer".equals(m.getRole())) {
    873                         outers.add(m.getWay());
    874                     } else if ("inner".equals(m.getRole())) {
    875                         inners.add(m.getWay());
    876                     }
    877                 }
    878             }
    879         }
    880     }
    881 
    882863    /**
    883864     * Tests if the {@code node} is inside the multipolygon {@code multiPolygon}. The nullable argument
     
    904885    public static boolean isPolygonInsideMultiPolygon(List<Node> nodes, Relation multiPolygon, Predicate<Way> isOuterWayAMatch) {
    905886        // Extract outer/inner members from multipolygon
    906         final MultiPolygonMembers mpm = new MultiPolygonMembers(multiPolygon);
    907         // Construct complete rings for the inner/outer members
    908         final List<MultipolygonBuilder.JoinedPolygon> outerRings;
    909         final List<MultipolygonBuilder.JoinedPolygon> innerRings;
     887        final Pair<List<JoinedPolygon>, List<JoinedPolygon>> outerInner;
    910888        try {
    911             outerRings = MultipolygonBuilder.joinWays(mpm.outers);
    912             innerRings = MultipolygonBuilder.joinWays(mpm.inners);
     889            outerInner = MultipolygonBuilder.joinWays(multiPolygon);
    913890        } catch (MultipolygonBuilder.JoinedPolygonCreationException ex) {
    914891            Main.trace(ex);
     
    917894        }
    918895        // Test if object is inside an outer member
    919         for (MultipolygonBuilder.JoinedPolygon out : outerRings) {
     896        for (JoinedPolygon out : outerInner.a) {
    920897            if (nodes.size() == 1
    921898                    ? nodeInsidePolygon(nodes.get(0), out.getNodes())
     
    924901                boolean insideInner = false;
    925902                // If inside an outer, check it is not inside an inner
    926                 for (MultipolygonBuilder.JoinedPolygon in : innerRings) {
     903                for (JoinedPolygon in : outerInner.b) {
    927904                    if (polygonIntersection(in.getNodes(), out.getNodes()) == PolygonIntersection.FIRST_INSIDE_SECOND
    928905                            && (nodes.size() == 1
  • trunk/test/unit/org/openstreetmap/josm/io/GeoJSONWriterTest.java

    r10171 r10817  
    33
    44import static org.junit.Assert.assertEquals;
     5import static org.junit.Assert.assertTrue;
    56
    67import org.junit.BeforeClass;
    78import org.junit.Test;
    89import org.openstreetmap.josm.JOSMFixture;
     10import org.openstreetmap.josm.TestUtils;
    911import org.openstreetmap.josm.data.coor.LatLon;
    1012import org.openstreetmap.josm.data.osm.DataSet;
     
    1214import org.openstreetmap.josm.gui.layer.OsmDataLayer;
    1315import org.openstreetmap.josm.gui.preferences.projection.ProjectionPreference;
     16
     17import java.io.FileInputStream;
    1418
    1519/**
     
    6670                "}").replace("'", "\""), writer.write().trim());
    6771    }
     72
     73    /**
     74     * Unit test for multipolygon
     75     */
     76    @Test
     77    public void testMultipolygon() throws Exception {
     78        try (FileInputStream in = new FileInputStream(TestUtils.getTestDataRoot() + "multipolygon.osm")) {
     79            DataSet ds = OsmReader.parseDataSet(in, null);
     80            final OsmDataLayer layer = new OsmDataLayer(ds, "foo", null);
     81            final GeoJSONWriter writer = new GeoJSONWriter(layer, ProjectionPreference.wgs84.getProjection());
     82            assertTrue(writer.write().contains("MultiPolygon"));
     83        }
     84    }
    6885}
Note: See TracChangeset for help on using the changeset viewer.