-
Notifications
You must be signed in to change notification settings - Fork 944
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
New module @turf/line-offset #729
Conversation
@rowanwins Can you provide more information in your Initial Description, that way when we reference this PR we have all the information available for the module at the top. Use this as a rough example: #700
You need to upgrade the benchmark version (v1.0.0 has issues with ES6 syntax). $ yarn add --dev tape benchmark |
packages/turf-line-offset/bench.js
Outdated
} | ||
}; | ||
|
||
var route = JSON.parse(fs.readFileSync(__dirname + '/test/fixtures/route.geojson')); |
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.
Try to make the new benchmark.js
files as minimalistic as possible, use a few of the latest modules as examples:
https://github.com/Turfjs/turf/blob/0cfbda88456a89c7cd988ac17afd6056a42f59a8/packages/turf-rhumb-distance/bench.js
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.
Doh didn't push the right benchmark file!
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.
:) nice
packages/turf-line-offset/index.d.ts
Outdated
/** | ||
* http://turfjs.org/docs/#lineOffset | ||
*/ | ||
declare function lineOffset(line: LineString | GeoJSON.LineString, distance: number, units?: string): 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.
Recently I've been adding the Units
types pulled from @turf/helpers
.
https://github.com/Turfjs/turf/blob/0cfbda88456a89c7cd988ac17afd6056a42f59a8/packages/turf-rhumb-distance/index.d.ts
packages/turf-line-offset/index.js
Outdated
* Takes a {@link LineString|line} and returns a {@link LineString|line} at offset by the specified distance. | ||
* | ||
* @name lineOffset | ||
* @param {Feature<LineString>|Geometry<LineString>} line input line |
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.
You can group the JSDoc types together
/**
* @param {Geometry|Feature<LineString>} line input line
packages/turf-line-offset/index.js
Outdated
seg2Coords[0][1] = int.y; | ||
} | ||
}); | ||
var finalCoords = segments.map(function (segment) { |
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 be worth using forEach
or for
loop since it will be significantly faster than using map
(~30-40% faster - Use benchmark to get an exact %)
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.
could be faster, although the benchmark is performing 600,000 operations per second so we're talking about shaving milli-milli-milli-mill seconds :) Can probably be tackled in the overall refactoring of those loops
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'll have a look at the refactoring, sometimes it's not worth it, but sometimes it's an easy change for a pretty significant performance increase. Not a deal breaker whatsoever
packages/turf-line-offset/index.js
Outdated
coordEach(line, function (currentCoords, currentIndex) { | ||
if (currentIndex !== coords.length - 1) { | ||
var outCoords = processSegment(currentCoords, coords[currentIndex + 1], offsetDegrees); | ||
segments.push(outCoords); |
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.
Iterating over all coords, then iterating over all segments, then iterating over segments again.
Looks like this could be done by only iterating your coordinates once.
You'll need to convert this code block into it's own method and use a callback
to prevent the double iterations.
I can tackle this if you don't feel comfortable using callbacks
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.
Yeah it is a bit messy at the moment, I'll probably wont get a chance to look at it for another few nights so if you've got time and want to tackle it go nuts :)
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.
Will do :) I'll have a look at it more tomorrow, it's a good starting point so far!
+1
packages/turf-line-offset/index.js
Outdated
return segment[0]; | ||
}); | ||
finalCoords.push(segments[segments.length - 1][1]); | ||
return lineString(finalCoords); |
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.
Should we also translate the properties from the input LineString?
packages/turf-line-offset/index.js
Outdated
return lineString(finalCoords); | ||
}; | ||
|
||
// Inspiration taken from http://stackoverflow.com/questions/2825412/draw-a-parallel-line |
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.
👍 Nice reference
"offset" | ||
], | ||
"author": "Turf Authors", | ||
"license": "MIT", |
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.
Add yourself as contributors
.
}, | ||
"homepage": "https://github.com/Turfjs/turf", | ||
"devDependencies": { | ||
"benchmark": "^1.0.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.
Upgrade benchmark
& tape
I'll upgrade all devDependencies in TurfJS
"@turf/helpers": "^4.1.0", | ||
"@turf/invariant": "^4.2.0", | ||
"@turf/meta": "^4.2.0", | ||
"intersection": "0.0.1" |
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'd drop intersection
in favor of using @turf/line-intersect
.
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.
intersection
works out where lines would intersect based on their direction, even if they don't actually intersect, eg it works irrespective of length. See this interactive example.
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.
Oooohh, yes that's completely different, I wonder if it's worth including that logic directly in Turf. I'm not a big fan of external dependencies, especially if it can be refactored in less than 30-50 LOC
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.
yeah it probably could be included, I had a bit of a look at the source code and it's not huge but it was enough of a hassle that I thought I'd pull in the external first just to test the concept first.
packages/turf-line-offset/index.js
Outdated
if (index !== segments.length - 1) { | ||
var seg2Coords = segments[index + 1]; | ||
var seg1 = {start: {x: segment[0][0], y: segment[0][1]}, end: {x: segment[1][0], y: segment[1][1]}}; | ||
var seg2 = {start: {x: seg2Coords[0][0], y: seg2Coords[0][1]}, end: {x: seg2Coords[1][0], y: seg2Coords[1][1]}}; |
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.
Pretty sure this can be handle by @turf/line-intersect
.
We could include Array<Array<number>>
support in line-intersect
(geometry.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.
See comment above about how the intersect
module differs from line-intersect
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.
+1 yep makes sense
"devDependencies": { | ||
"benchmark": "^1.0.0", | ||
"tape": "^3.5.0", | ||
"@turf/helpers": "^4.1.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.
Missing write-json-file
& load-json-file
devDependencies
To make the tests pass you might need to wrap the results with |
Gday @DenisCarriere Have just updated the whole looping approach and its now much tidier I think. Only thing is the benchmark results look like they are slower than before but in reality I think it's now twice as fast, either a) my laptop is running on battery and goes slower, or b) I changed a benchmark test parameter. Anyway I've run the same benchmark test on both variations on the code this evening and the latest commit is twice as fast as the old. Cheers |
Woot! Passing tests, one of the reasons why I decided to build |
Updated to support lines with only 1 segment and lines that are completely straight (horizontal and vertical). |
PS Turns out that having my battery plugged in does effect the performance of my benchmark tests!
|
👍 Yea the benchmark results are very dependent on CPU performance, but it's a good gage of performance. Also closing 10+ Google Chrome browser windows help 🤓 |
@rowanwins Just an update on this PR, once we get a few other geometries supported this will be ready to merge.
|
Gday @DenisCarriere It did just cross my mind that this could conceivable be used as a replacement for a buffer module, particularly for polys... Not sure how performance would go but worth a quick trial. |
Dropping Polygon or MultiPolygon support since this algorithm is not designed for this type of offset. Below are examples of attempts using Red: Input
CC: @rowanwins |
I thought so too! Minus the crazy outputs! Lets stick with LineStrings on this module. I really like it so far, but not with Polygons. |
@rowanwins Added MultiLineString support, I'm ready to merge this if ever you are 👍 with it. |
@rowanwins Another issue that can be addressed at a later time is the Equidistance offset, same issue we had with Issue in Northern Latitudes50km offset north/south directions, 12km offset west/east direction https://github.com/Turfjs/turf/blob/bc34c926a11a424992b9e08643603fa81d54329f/packages/turf-line-offset/test/out/northern-line.geojson |
merge away. I might have a tinker with the polygon issue because im sure the code is there, just might need a bit of refactoring |
@rowanwins @DenisCarriere I believe the output with (Multi)Polygons is actually the correct/expected result for this module, as it creates a parallel (and elongated/shortened) line alongside the original one. A Polygon just happens to be a closed line, however the module I think should not connect the start and end points of the resulting line. |
👍 Agreed
Agreed, only supporting LineString would be best for this module. |
@rowanwins @DenisCarriere I believe these formulas might help to correct the distortion here and in other modules. |
That's what was used for |
Hi @DenisCarriere & @stebogit One thought on the whole distortion issue, my understanding is that projections have been designed differently, some projections are designed for preserving shape, while others are designed to preserve area etc etc With the lines @DenisCarriere references above we're making that judgement for ppl as to what the best projection is (one that preserves shape). D3 actually supports a bunch which are useful for different purposes. Rather than us forcing an choice on people perhaps we should be getting them to pass in their data with the projection they deem appropriate? Thoughts? |
I personally don't have experience with projections different from (Web)Mercator (i.e. Google Maps).
and Just my thought. |
👎 TurfJS shouldn't be providing projection support. @w8r explained it well in #660 (comment).
|
@rowanwins Using that equidistance projection trick will solve the offset being different. This isn't a projection "visual effect", the offset is legitimately different (12km & 50km). |
Going to merge this since it's 95% complete, only tiny issue is situations up north (can be done in the next release or a new PR). |
Gday @DenisCarriere Just checking back in on the projection discussion. I wasn't proposing for turf to support projections, there is already proj4js to do that. I just wanted to check that by forcing a projection during a process like |
Agree we should do the same as the buffer, however we should make the project/reproject process as a module or external dependency. |
New Module
@turf/line-offset
Adds a new lineOffset module as per this issue. Basically takes an input line and returns a new line offset by the distance.
Geometry Support
Geometry NOT supported
@turf/circle
?)@turf/circle
?)@turf/buffer
/@turf/transform-scale
)@turf/buffer
/@turf/transform-scale
)To-Do
JSDocs
Examples
npm test
at the sub modules where changes have occurred.npm run lint
to ensure code style at the turf module level.