-
Notifications
You must be signed in to change notification settings - Fork 942
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Initial commit of boolean DE-9IM module #707
Conversation
😄 Oh my! Looks great so far (quickly browsed the repo), I'll dive into further when I have hours and hours to dedicate to this 👍 |
Looks good. Just a thought, the motivation behind bringing up the |
@dpmcmlxxvi Only having to deal with 3x3 makes a lot sense! |
Thanks for the comment/idea @dpmcmlxxvi , I wasn't sure how much overhead flattening the features would add in terms of performance so it's probably worth setting up some benchmark tests, it would be nice to cater for fewer scenarios :) |
Cross now mostly done... |
👍 Keep them coming! Next steps would be isolate the modules to be in their own repo |
@rowanwins you might want to look into switch cases instead of a ton of For example (using @dpmcmlxxvi's flatten approach): var multiFeature1 = flatten(feature1);
var multiFeature2 = flatten(feature2);
geomEach(multiFeature1, function (geom1) {
geomEach(multiFeature2, function (geom2) {
switch (geom1.type) {
case 'Point':
switch (geom1.type) {
case 'Point':
case 'LineString':
case 'Polygon':
}
case 'LineString':
switch (geom1.type) {
case 'Point':
case 'LineString':
case 'Polygon':
}
case 'Polygon':
switch (geom1.type) {
case 'Point':
case 'LineString':
case 'Polygon':
}
}
});
}); Depending on which boolean operations, you might want to use |
@rowanwins Just noticed var deepEqual = require('deep-equal');
deepEqual(MultiPoint.geometry.coordinates[i], Point.geometry.coordinates) |
- Support Geometry Objects - Use deep-equal - Use Switches CC: @rowanwins
|
||
// TO DO - Work out how to check if line is in line (can potentially use line overlap module) | ||
// Also need to make sure lines are exactly the same, eg the second must be smaller than the first | ||
function isLineOnLine(LineString1, LineString2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
t.equal(contains(line1, line3), false, 'A line fails that lies partially outside the other line');
Test fail because line should be completely on line (returns true because of partial match).
Might be worth adding a isLineCompletelyOnLine(line1, line2)
method.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Gday @DenisCarriere
As a general comment I feel like the line booleans are the trickiest to get right at this stage, acknowledge that there is some work needed there
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Never said it was easy 👍 Keep it up!
Just on the tests @DenisCarriere I dont think they are really too bad, if you want to test a individual boolean simply I think it would actually be a fair bit more overhead to split them all out. |
I disagree, it's going to be a nightmare when there's 9 different test cases all in one repo. Especially when we start adding fixtures to each repo (larger GeoJSON). Also, we might end up including a test fixture folder for true/false.
That way it's easier for someone to submit a random GeoJSON that has the wrong expected output (true/false). |
Also for modules that depend on other modules, we would simply just add it as a dependency like |
return false; | ||
} | ||
} | ||
return foundAMatch > 0 && foundAMatch < MultiPoint1.coordinates.length; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The second condition here excludes the two geometries being equal. Not sure if that is actually in the definition of contains
. According to DE-9IM the definition is:
- No point of
MultiPoint2
must be in the exterior ofMultiPoint1
- At least 1 point in the interior of
MultiPoint2
must be in the interior ofMultiPoint1
Since the points themselves are their own interior, the two being equal would seem to meet that criteria. Could be wrong, the DE-9IM definitions are sometimes tricky to parse.
} | ||
|
||
// http://stackoverflow.com/a/11908158/1979085 | ||
function isPointOnLine(LineString, Point) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be allowing the point to land on the line end points. That would place the point on the line boundary and therefore no point lies on the interior of the line (see above definition of contains). I think the end points should be excluded.
return output; | ||
} | ||
|
||
function isMultiPointOnLine(LineString, MultiPoint) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same issue as above. No check that any point is in the interior of line. As you probably have noticed already the edge cases are the real pain when implementing these booleans functions.
return inside(Point, Polygon); | ||
} | ||
|
||
function isMultiPointInPoly(Polygon, MultiPoint) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same interior point issue as above.
return output; | ||
} | ||
|
||
function isLineInPoly(Polygon, Linestring) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this approach will only work for convex polygons. Concave or polygons with holes can fail this. Think of a donut with a line that only crosses the hole.
I think it would be safer to first check if the line intersects the polygon boundaries (including holes). If it doesn't then check that at least one point is in polygon interior.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
👍 Polygons with holes will be hard to implement, @rowanwins I think at first lets just get it working with only outer rings and afterwards we work on the inner ring validation (donuts).
|
||
// See http://stackoverflow.com/a/4833823/1979085 | ||
// TO DO - Need to handle if the polys are the same, eg there must be some outer coordinates different | ||
function isPolyInPoly(Polygon1, Polygon2) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this has the same issue as isLineInPoly
above.
packages/turf-boolean/equal.js
Outdated
*/ | ||
|
||
module.exports = function (feature1, feature2) { | ||
return checkIfDeepCoordArraysMatch(feature1.geometry.coordinates, feature2.geometry.coordinates) && feature1.geometry.type === feature2.geometry.type; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Might want to add a warning that this is only testing that the coordinates are identical. It is not testing that the geometries are equal. For example, the lines [[0,0], [2,0]]
and [[0,0], [1,0], [2,0]]
are equal but don't have identical coordinates.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This one sounds like it would be real tricky to write. Would be worth including as a test case or add it to the JSDocs description.
packages/turf-boolean/index.js
Outdated
* var along = turf.contains(line, point); | ||
*/ | ||
|
||
module.exports = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@DenisCarriere Just wondering what the policy is on namespacing functionality. I thought I recalled reading in some other thread that Turf enforced a flat structure and namespacing functionality was discouraged. May be mis-remembering.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agree, that's why I suggested to have all the boolean
modules within their own unique repos. I've only converted @turf/boolean-contains
so far from this PR.
@rowanwins Lets stick with 1 PR per boolean
operation & each operation should be within their own unique repo, @turf/boolean
can group them all up afterwards.
packages/turf-boolean/touch.js
Outdated
} | ||
} | ||
} | ||
return onStartOrEnd === 1 && pointIsSomewhereOnLine === 0; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just a nit-pick. You can probably return inside the loop if the second criteria is broken. That way you don't iterate through all the points if you don't need to. Also, I think the first criteria should read onStartOrEnd > 0
. It could be both ends that are touched.
@rowanwins @DenisCarriere Just a quick note on |
Just as a heads up on this @DenisCarriere I've created a For example Any qualms with this approach? I'm not sure how many functions will end up in here but probably half a dozen I'd guess. |
👍 I like the idea of having |
@rowanwins I would refrain from adding any
Example: return !isPointOnLine(geom2, geom1) ? true : false; //eslint-disable-line no-unneeded-ternary Refactored to: return !isPointOnLine(geom2, geom1) || false;
return !isPointOnLine(geom2, geom1); |
ah that's how you get rid of that stupid error, thanks @DenisCarriere , learn something new everyday! |
packages/turf-boolean-cross/test.js
Outdated
@@ -17,7 +17,8 @@ test('turf-boolean-cross', t => { | |||
|
|||
t.equal(cross(line1, line2), true, 'True if lines cross'); | |||
t.equal(cross(line1, line3), false, 'False if lines cross'); | |||
t.equal(cross(line1, line4), false, 'False if lines only touch'); | |||
// **== ERROR ==** | |||
// t.equal(cross(line1, line4), false, 'False if lines only touch'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just wondering if the test library provides a skip method to gracefully skip a test so that it still shows up listed but just not executed. That way it doesn't get forgotten but won't cause a failure. I know mocha lets you do that by just throwing in .skip(
before the test. According to the tape docs it looks like they do though I haven't tried it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's most likely a better idea, there's still tons of tests still missing, lots of work left to do
Prevents accidental dotfiles from being read by `fs`
- Update JSDocs - Add bench.js - Add index.d.ts - Add LICENSE
@rowanwins have you tried this library? geojson-equality which was used in |
@rowanwins Added a few extra params to /**
* @name equal
* @param {Geometry|Feature<any>} feature1 GeoJSON Feature or Geometry
* @param {Geometry|Feature<any>} feature2 GeoJSON Feature or Geometry
* @param {Boolean} [direction=false] direction of LineString or Polygon (orientation) is ignored if false
* @param {number} [precision] coordinate decimal precision Tests test('turf-boolean-equal -- reduce coordinate precision', t => {
const pt1 = point([30.2, 10]);
const pt2 = point([30.22233, 10]);
t.true(equal(pt1, pt2, false, 1));
t.false(equal(pt1, pt2, false, 3));
t.end();
});
test('turf-boolean-equal -- apply direction (orientation) rule', t => {
const line1 = lineString([[30, 10], [10, 30], [40, 40]]);
const line2 = lineString([[40, 40], [10, 30], [30, 10]]);
t.true(equal(line1, line2, true));
t.false(equal(line1, line2, false));
t.end();
}); |
* added fiji + resolute-bay tests to translate * added fiji + resolute-bay test to rotate * Add new modules to Turf core * v4.4.0 * Fix tests for blank dependencies * Add inside tests * New @turf/isolines based on MarchingSquares.js (#781) * replaced conrec with marchingsquare * fixed script and tests * updated bench * updated readme * updated package.json * updated index.d.ts * introduced suggested changes * added zProperty to index.d.ts * minor doc change * Updates to Isolines - Divide properties={} to two params - Simplify Catch error logic (index.js) - Update Typescript definition - Add types.ts to test Typescript definition - Add single task to benchmark - Update Yarn.lock - Simplify bench & test logic - Save fixtures out to GeoJSON CC: @stebogit * perIsoline overlaps properties from toAllIsolines * Update Readme * Buffer (#786) Buffer - Drop circle buffer operation * removed turf-isolines old test files * copied files from repo * refactored index and test * updated README * updated index.d.ts and bench * added yarn.lock * removed Polygon as accepted input; added array test; * added masking feature to turf-grid * added throws tests * updated yarn.lock * added contributor * Resolves #785 * Add test fixture * Update complex unkink-polygon * Update name to boolean-clockwise * Declare input as line instead of Feature
@rowanwins Don't mind if we close this PR, I'm pretty sure you have this stored locally and we can leave the branch active until we port over everything. |
Gday @DenisCarriere
As discussed attached is my current approach on a boolean style module for DE-9IM. I've been using the doco on this page as my guide so far.
DE-9IM modules
contains
)Extra Testing
[[0,0], [2,0]]
and[[0,0], [1,0], [2,0]]
are equal but don't have identical coordinates.There are a few helper style functions within the
helpers.js
file.Anyway happy for any feedback. I'm sure some of the algorithms could be improved, they are pretty brute force at the moment.
Cheers
Rowan