Skip to content
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 segmentEach & segmentReduce (@turf/meta) #863

Merged
merged 7 commits into from
Jul 26, 2017
Merged

Conversation

DenisCarriere
Copy link
Member

@DenisCarriere DenisCarriere commented Jul 24, 2017

New segmentEach & segmentReduce (@turf/meta)

To-Do

  • Replace segmentReduce in @turf/line-distance.
  • Use of turf.polygon & turf.featureCollection in JSDocs @example
  • Compact If/Else statements
  • Add explanation that (Multi)Point will be ignored
  • Ignore (Multi)Point from being processed (should speed the iterating process).
  • Add MultiPoint to tests
  • Add internal lineString method
  • add subIndex
  • test for skipped `subIndex

segmentEach

/**
 * Callback for segmentEach
 *
 * @callback segmentEachCallback
 * @param {Feature<LineString>} [currentSegment] The current segment being processed.
 * @param {number} [currentIndex] The index of the current element being processed in the array, starts at index 0.
 * @param {number} [currentSubIndex] The subindex of the current element being processed in the
 * array. Starts at index 0 and increases for each iterating line segment.
 * @returns {void}
 */

/**
 * Iterate over 2-vertex line segment in any GeoJSON object, similar to Array.forEach()
 * (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
 *
 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON
 * @param {Function} callback a method that takes (currentSegment, currentIndex, currentSubIndex)
 * @returns {void}
 * @example
 * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);
 *
 * // Iterate over GeoJSON by 2-vertex segments
 * turf.segmentEach(polygon, function (currentSegment, currentIndex, currentSubIndex) {
 *   //= currentSegment
 *   //= currentIndex
 *   //= currentSubIndex
 * });
 *
 * // Calculate the total number of segments
 * var total = 0;
 * var initialValue = 0;
 * turf.segmentEach(polygon, function () {
 *     total++;
 * }, initialValue);
 */
function segmentEach(geojson, callback) {

segmentReduce

/**
 * Callback for segmentReduce
 *
 * The first time the callback function is called, the values provided as arguments depend
 * on whether the reduce method has an initialValue argument.
 *
 * If an initialValue is provided to the reduce method:
 *  - The previousValue argument is initialValue.
 *  - The currentValue argument is the value of the first element present in the array.
 *
 * If an initialValue is not provided:
 *  - The previousValue argument is the value of the first element present in the array.
 *  - The currentValue argument is the value of the second element present in the array.
 *
 * @callback segmentReduceCallback
 * @param {*} [previousValue] The accumulated value previously returned in the last invocation
 * of the callback, or initialValue, if supplied.
 * @param {Feature<LineString>} [currentSegment] The current segment being processed.
 * @param {number} [currentIndex] The index of the current element being processed in the
 * array. Starts at index 0, if an initialValue is provided, and at index 1 otherwise.
 * @param {number} [currentSubIndex] The subindex of the current element being processed in the
 * array. Starts at index 0 and increases for each iterating line segment.
 */

/**
 * Reduce 2-vertex line segment in any GeoJSON object, similar to Array.reduce()
 * (Multi)Point geometries do not contain segments therefore they are ignored during this operation.
 *
 * @param {FeatureCollection|Feature|Geometry} geojson any GeoJSON
 * @param {Function} callback a method that takes (previousValue, currentSegment, currentIndex)
 * @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
 * @returns {void}
 * @example
 * var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);
 *
 * // Iterate over GeoJSON by 2-vertex segments
 * turf.segmentReduce(polygon, function (previousSegment, currentSegment, currentIndex, currentSubIndex) {
 *   //= previousSegment
 *   //= currentSegment
 *   //= currentIndex
 *   //= currentSubIndex
 *   return currentSegment
 * });
 *
 * // Calculate the total number of segments
 * var initialValue = 0
 * var total = turf.segmentReduce(polygon, function (previousValue) {
 *     previousValue++;
 *     return previousValue;
 * }, initialValue);
 */
function segmentReduce(geojson, callback, initialValue) {

Benchmark

Based on a random Polygon

Note: A bit slower than most meta features because internally it's using flattenEach & coordReduce.

segmentEach   - polygon x 884,579 ops/sec ±2.06% (82 runs sampled)
segmentReduce - polygon x 770,112 ops/sec ±1.65% (85 runs sampled)

Copy link
Collaborator

@stebogit stebogit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 @DenisCarriere
I believe the only issue might be excluding (Multi)Point as input.

var currentIndex = 0;
flattenEach(geojson, function (feature) {
coordReduce(feature, function (previousCoords, currentCoords) {
var currentSegment = {
Copy link
Collaborator

@stebogit stebogit Jul 25, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DenisCarriere I bet it's a choice not to have dependencies in turf-meta. 👍
Otherwise you could use

var currentSegment = lineString([previousCoords, currentCoords], feature.properties || {});

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes... @turf/meta has no dependencies, otherwise I would use that

* @param {*} [initialValue] Value to use as the first argument to the first call of the callback.
* @returns {void}
* @example
* var polygon = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same as the above example

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No dependencies for meta module, so for this one we can exclude the helpers functions. If we change it, we should change it for the entire JSDoc's module

function segmentReduce(geojson, callback, initialValue) {
var previousValue = initialValue;
segmentEach(geojson, function (currentSegment, currentIndex) {
if (currentIndex === 0 && initialValue === undefined) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks cleaner to me (learned from you 😜 ):

if (currentIndex === 0 && initialValue === undefined) previousValue = currentSegment;
else previousValue = callback(previousValue, currentSegment, currentIndex);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes & No.. :P since there's Sooo much going on for 1 line of code, they were kept separated.

But we can switch it to that, this got copy-pasted from the previous reduce functions.

* @param {Function} callback a method that takes (currentSegment, currentIndex)
* @returns {void}
* @example
* var polygon = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe you could use

var polygon = turf.polygon([[[-50, 5], [-40, -10], [-50, -10], [-40, 5], [-50, 5]]]);

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

👍 Agreed, I really do prefer these "shorter" examples, more focused on the code itself and not 10 lines of defining a GeoJSON polygon.

* total++;
* }, initialValue);
*/
function segmentEach(geojson, callback) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this gonna work on (Multi)Points?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It works... but Multi Points aren't lines so no segments will be returned, we can include it in the test cases.

* return previousValue;
* }, initialValue);
*/
function segmentReduce(geojson, callback, initialValue) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also here, is it gonna work on (Multi)Point? Probably we need to exclude them as valid inputs

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't need to throw an error, because that would prevent having mixed inputs and it would be really aggravating having to pre-filter your data.

Points & MultiPoints are ignored which is defined in the description.

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?? Oops, I thought I added that explanation in the JSDocs (about the (Multi)Point), I guess I must've forgotten about it.

👍 Good catch

@DenisCarriere
Copy link
Member Author

DenisCarriere commented Jul 25, 2017

@stebogit Added this to my 'To-Do'

  • Use of turf.polygon in JSDocs @example
  • Compact If/Else statements
  • Add explanation that (Multi)Point will be ignored
  • Ignore (Multi)Point from being processed (should speed the iterating process).
  • Add MultiPoint to tests
  • Add internal lineString method

- [x]  Use of `turf.polygon` in JSDocs `@example`
- [x] Compact If/Else statements
- [x] Add explanation that (Multi)Point will be ignored
- [x] Ignore (Multi)Point from being processed (should speed the iterating process).
- [x]  Add MultiPoint to tests
- [x] Add internal `lineString` method
CC: @stebogit
@DenisCarriere
Copy link
Member Author

Thanks @stebogit for the review, I needed that 👍

* @param {Object} properties Properties
* @returns {Feature<LineString>} GeoJSON LineString Feature
*/
function lineString(coordinates, properties) {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DenisCarriere I don't get why exactly we need/want to have no dependencies in meta, if we would actually need helpers for instance. Is this faster maybe?

Copy link
Collaborator

@stebogit stebogit left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wonderful @DenisCarriere! As soon as you merge this to master I'll use these functions in @turf/boolean-overlap!

* var initialValue = 0;
* turf.segmentEach(polygon, function () {
* total++;
* }, initialValue);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@DenisCarriere I believe initialValue doesn't belong in here (only for segmentReduce). Does it?

@stebogit
Copy link
Collaborator

@DenisCarriere I believe in segmentEach having an index and subIndex of the feature from which the current segment is pulled from would be useful, like in flattenEach

@DenisCarriere
Copy link
Member Author

DenisCarriere commented Jul 26, 2017

👍 subIndex would be pretty useful.

Also we should test for the skipped indexes (ex: FeatureCollection of points & polygons) the point index will be skipped and not 1++.

  • add subIndex
  • test for skipped subIndex

@DenisCarriere
Copy link
Member Author

DenisCarriere commented Jul 26, 2017

@stebogit Implemented the subIndex callback property 👍 Good call on that one.

@DenisCarriere DenisCarriere merged commit a05c109 into master Jul 26, 2017
@DenisCarriere DenisCarriere deleted the segmentEach branch July 26, 2017 18:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants