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

More LineString iterators #705

Merged
merged 12 commits into from
Jan 6, 2022
Merged

Conversation

urschrei
Copy link
Member

@urschrei urschrei commented Dec 28, 2021

  • I agree to follow the project's code of conduct.
  • I added an entry to CHANGES.md if knowledge of this change could be valuable to users.

This PR adds the iter and iter_mut methods on LineString (previously only available via its public .0 field), yielding Coordinates, and adds an into_coordinates method, complementing into_points. It also adds into_iter for borrowed LineStrings.

Some of these methods overlap with those available in geos CoordsIter trait, but:

  • Not all geo-types library users are necessarily geo users
  • The lack of direct iter methods is a constant papercut in my opinion.

@urschrei urschrei force-pushed the more_linestring_iterators branch from c690c41 to ee28a36 Compare December 28, 2021 12:54
Hitherto, we only supported looping as opposed to direct iteration (though since .0 is public, we could access the underlying vector anyway)

This allows explicit (mutable) iteration via a CoordinatesIter struct
@urschrei urschrei force-pushed the more_linestring_iterators branch from 87ca144 to 476a6cf Compare December 28, 2021 17:41
@lnicola
Copy link
Member

lnicola commented Dec 28, 2021

Can iter use the same impl Iterator trick?

@urschrei
Copy link
Member Author

Can iter use the same impl Iterator trick?

Done. I don't think there's a way to leverage it for IntoIter though, otherwise we could do away with the helper struct.

geo-types/src/line_string.rs Show resolved Hide resolved
geo-types/src/line_string.rs Outdated Show resolved Hide resolved
geo-types/src/line_string.rs Outdated Show resolved Hide resolved
urschrei and others added 2 commits December 31, 2021 12:33
Co-authored-by: Corey Farwell <coreyf@rwell.org>
@urschrei
Copy link
Member Author

Implemented all yr suggestions @frewsxcv.

@frewsxcv frewsxcv self-requested a review January 2, 2022 18:59
pub fn points_iter(&self) -> PointsIter<T> {
PointsIter(self.0.iter())
}

/// Return the coordinates of a `LineString` as a `Vec` of `Point`s
/// Return an iterator yielding the members of a [`LineString`] as [`Coordinate`]s
pub fn iter(&self) -> impl Iterator<Item = &Coordinate<T>> {
Copy link
Member

Choose a reason for hiding this comment

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

Sorry to bikeshed on naming, but... iter strikes me as a little off.

In contrast, these usages of iter all make sense to me:

multi_point.iter() -> impl Iterator<Item = &Point<T>>
multi_line_string.iter() -> impl Iterator<Item = &LineString<T>>
multi_polygon.iter() -> impl Iterator<Item = &Polygon<T>>
vec_of_strings.iter() => -> impl Iterator<Item = &String>

For a collection of Foos, iter() will yield Foo. But I don't think of a LineString as a collection of Coordinates (it happens to look that way on disk, but so do all of our geometries).

What would you think of pub fn coords(&self) or coords_iter()?

Copy link
Member

Choose a reason for hiding this comment

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

fn coords() might match nicely with the existing:

/// Return an iterator yielding one [Line] for each line segment in the [`LineString`].
pub fn lines(&'_ self) -> impl ExactSizeIterator + Iterator<Item = Line<T>> + '_ { }

/// An iterator which yields the coordinates of a [`LineString`] as [Triangle]s
pub fn triangles(&'_ self) -> impl ExactSizeIterator + Iterator<Item = Triangle<T>> + '_ { }

Copy link
Member Author

Choose a reason for hiding this comment

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

Hmmmm. I initially added iter() because I expected LineString to have it, mostly when writing internal code. But you're absolutely correct in that the choice stems purely from my own expectations, which are at least partly rooted in the fact that we had already added IntoIter so we could loop over Coordinates, and I was annoyed that we could loop but not explicitly iter().

I'm open to adding coords() instead, but should we then consider deprecating points_iter() in favour of points() in an effort to be consistent?

If we do, I'll probably rewrite the introductory docs a bit to note that we offer four iterators, but note that loops will yield Coordinate (which should be thought of as the default?)

Copy link
Member

Choose a reason for hiding this comment

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

Oh geeze, software is so hard.

Honestly, in hindsight, I think it's a little weird that you can do this at all:

for x in &line_string { 
    ...
}

But anyway, that ship sailed long ago, and maybe consistency is more important at this point. Thanks for bringing up the context, your change seems pretty reasonable in light of that.

Would it be crazy to do all of the following?

  • Keep the iter() impl as you have it since it complements the existing into_iter(),
    • Alternatively, we could remove the into_iter impl from line_string, but I hesitate to break things, and I don't personally think it's doing much harm as is, just that there are clearer ways to write it.
  • add line_string.coords() which does the exact same thing as line_string.iter() but reads more clearly IMO
  • add points() / deprecate points_iter()
    • I think it's pretty idiomatic to omit the _iter suffix when returning transformations of the inner data - e.g. hash_map.values() and hash_map.values_mut() not hash_map.values_iter() and hash_map.values_iter_mut() same with string.chars() not string.chars_iter().

I know it's a real laundry list of things... so if you're in spirit on board, but don't have the time to do it, feel free to punt on some of it.

@urschrei
Copy link
Member Author

urschrei commented Jan 4, 2022

Pulling #705 (review) comments out into the main thread for discussion. @michaelkirk I've diverged slightly from your suggestion: I've removed iter() in favour of coords(), and re-written the docs to:

  • Show all LineString creation methods / mechanisms;
  • list all five available iterators (coords[_mut], points, lines, and triangles);
  • note that our IntoIterator impl yields Coordinates when looping (impls can't be marked as deprecated, unfortunately, and I agree that it's too late to do anything about its presence now)
  • Show LineString decomposition methods (returning Vecs of Coordinates or Points)

I think I now prefer the lack of iter() combined with clearer docs: it means that you have to be explicit about what you want to iterate over.

@michaelkirk
Copy link
Member

I think I now prefer the lack of iter() combined with clearer docs: it means that you have to be explicit about what you want to iterate over.

LGTM! I think this turned out rather nicely! Thanks for allowing the back-and-fourth.

impl<T: CoordNum, IC: Into<Coordinate<T>>> FromIterator<IC> for LineString<T> {
fn from_iter<I: IntoIterator<Item = IC>>(iter: I) -> Self {
LineString(iter.into_iter().map(|c| c.into()).collect())
}
}

/// Iterate over all the [Coordinate](struct.Coordinates.html)s in this `LineString`.
/// Iterate over all the [`Coordinate`]s in this [`LineString`].
Copy link
Member

Choose a reason for hiding this comment

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

Thanks for cleaning all these up!

@urschrei
Copy link
Member Author

urschrei commented Jan 4, 2022

I think this turned out rather nicely!

I think so too! I'm gonna leave it open for a day in case anyone else wants to kick the tires, otherwise I'll merge tomorrow.

@urschrei
Copy link
Member Author

urschrei commented Jan 6, 2022

bors try

bors bot added a commit that referenced this pull request Jan 6, 2022
@bors
Copy link
Contributor

bors bot commented Jan 6, 2022

try

Build succeeded:

@urschrei
Copy link
Member Author

urschrei commented Jan 6, 2022

bors r+

@bors
Copy link
Contributor

bors bot commented Jan 6, 2022

Build succeeded:

@bors bors bot merged commit 4ce78ee into georust:master Jan 6, 2022
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants