bjornharrtell / jts2geojson

JTS from/to GeoJSON converter for Java
MIT License
141 stars 57 forks source link

Polygon with Holes does not support the right-hand rule #24

Closed Kirintale closed 2 years ago

Kirintale commented 7 years ago

Hi,

Thank you for writing this tool. I've been trying to use your tool for a polygon with a hole in it. Testing the result at (http://geojsonlint.com/).

In JTS polygons are declared such that the exterior contour will be clockwise and the holes will be anti-clockwise. GeoJson however operates on the opposite assumption that holes are anti-clockwise.

I don't think your code currently corrects for this deviation. Would an update be possible?

Thank you for any response.

bjornharrtell commented 7 years ago

Nice catch, I think it might be something to consider for the future. I have no currently planned free time to implement this myself though.

Kirintale commented 7 years ago

No worries. I just did a fork and a quick fix, to convert JTS to GeoJson (but not the reverse yet).

The code is detailed below. I'll update the fork and link to it when I work out how ^_^. Please excuse the bad naming conventions it was a rush job. But here are the correction required.

package org.wololo.jts2geojson;

import com.vividsolutions.jts.algorithm.CGAlgorithms;
import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.CoordinateArrays;

public class GeoJsonCoordinateUtilities {

/**
 * Rotates if necessary JTS coordinates for conversion to a GeoJson object. This is required as GeoJson expects
 * outer contours to be anti-clockwise and holes to be clockwise. JTS expects outer contours to be clockwise and
 * holes to be anti-clockwise
 * 
 * @param coordinates
 *            the polygon coordinates from a JTS object
 * @param representsHole
 *            true if the coordinates represent a hole.
 * @return polygon coordinates ready to be applied to GeoJson objects
 */
public static Coordinate[] correctPolygonCoordinateRotation(final Coordinate[] coordinates,
        final boolean representsHole) {

    if (isAntiClockwise(coordinates)) {
        // If the coordiantes are anti-clockwise, but they should represent a hole. Reverse their direction.
        if (representsHole) {
            CoordinateArrays.reverse(coordinates);
        }
    } else {
        // If the coordiantes are clockwise, but they should not represent a hole. Reverse their direction.
        if (!representsHole) {
            CoordinateArrays.reverse(coordinates);
        }
    }

    return coordinates;
}

/**
 * Checks if the co-ordinates are anti-clockwise.
 * 
 * @param coordinates
 *            The co-ordinates to test.
 * @return True if it is anti-clockwise.
 */
public static boolean isAntiClockwise(final Coordinate[] coordinates) {
    return CGAlgorithms.isCCW(coordinates);
}

}

Kirintale commented 7 years ago

In GeoJsonWriter

org.wololo.geojson.Polygon convert(Polygon polygon) {
    int size = polygon.getNumInteriorRing() + 1;
    double[][][] rings = new double[size][][];
    final Coordinate[] coordinates = polygon.getExteriorRing().getCoordinates();
    rings[0] = convert(GeoJsonCoordinateUtilities.correctPolygonCoordinateRotation(coordinates, false));
    for (int i = 0; i < size - 1; i++) {
        final Coordinate[] holeCoordinates = polygon.getInteriorRingN(i).getCoordinates();
        rings[i + 1] = convert(GeoJsonCoordinateUtilities.correctPolygonCoordinateRotation(holeCoordinates, true));
    }
    return new org.wololo.geojson.Polygon(rings);
}
bjornharrtell commented 7 years ago

Looks promising. Too bad there doesn't seem to be a force right hand rule or such in JTS. If you can make your fork into a reviewable PR I'll take a look when possible.

Kirintale commented 7 years ago

Yeah agreed.

https://github.com/Kirintale/jts2geojson

Is a link to my fork. How do I make it a reviewable PR?

One thing I should mention. On my fork I stripped back your pom file so it worked on my machine. Those changes you could likely ignore.

patzi commented 6 years ago

@Kirintale you would have to clean your repository from changes that don't belong to this problem, i.e. in a branch. And than you can easily create a pull see: https://help.github.com/articles/creating-a-pull-request/ If you are no longer interested I could maybe find some time to work on this.

bjornharrtell commented 2 years ago

Closing due to lack of activity.