Skip to content

Add possibility to group features for, drag / rotate / delete#104

Merged
zxwild merged 3 commits intogeoman-io:masterfrom
DeliaStrat:f/#90-group-features
Mar 4, 2026
Merged

Add possibility to group features for, drag / rotate / delete#104
zxwild merged 3 commits intogeoman-io:masterfrom
DeliaStrat:f/#90-group-features

Conversation

@lhapaipai
Copy link
Contributor

Group Features Support

Closes #90

group-features.prepared.mp4

Summary

This PR implements the ability to group features of different geometry types (e.g., Polygon and LineString) to move, rotate, or delete them together as a single unit. Previously, only MultiLineString and MultiPolygon could emulate this behavior because they are the same geometry type.

Problem

Users needed a way to group different geometry types representing a single complex shape and manipulate them together while keeping the shape in sync. For example, a Polygon with a LineString representing related elements should be movable and rotatable as a cohesive unit.

Solution

Features can now be grouped using a custom __gm_group property. When features share the same group identifier, they are treated as a single entity for transformation operations.

Features Implemented

  • Group Translation: Moving one feature in a group moves all features in that group together
  • Group Rotation: Rotating one feature in a group rotates all features around a common center
  • Group Deletion: Deleting one feature in a group deletes all features in that group
  • Grouped actions are automatically disabled if any feature in the group has that action disabled

Tests

  • Added tests for drag behavior but sometimes (with headless browser) there are some translation errors (some pixels)
  • Temporarily bypassing rotate tests for grouped features.
  • Added a small fix to hide left/right dev panels (which are great by the way) in test mode because the screen is too small and causes some tests to fail

Details

I needed to change the moveFeature implementation to use a LngLatDiff because the method could be called multiple times by different features.

I chose not to associate linkedFeatures directly as a property in the featureData but to compute the array when the drag/rotate/delete action begins for more flexibility. (Users can change the group shape property of one feature and they don't need to update the link between all featureData affected by the modification).

I found a broken portion of code in the EditRotate class

export class EditRotate extends BaseDrag {

  handleGmEdit(event: GmSystemEvent): MapHandlerReturnData {
    // some code

    if (event.action === 'marker_move' && event.lngLatStart && event.lngLatEnd) {
      if (event.markerData?.type === 'vertex') {
        this.moveVertex(event);
      } else {
        // Actually do nothing, can we safely remove this part or fix
        // the code if markerData type can be other than vertex when rotating.
        const lngLatDiff = getLngLatDiff(event.lngLatStart, event.lngLatEnd);
        this.moveSource(event.featureData, lngLatDiff);
      }

    }
  }
}

Conclusion

I hope this feature is of interest. Please let me know if there are any implementation details that could be improved.

@lhapaipai
Copy link
Contributor Author

lhapaipai commented Dec 17, 2025

I've added a little extra code to implement the selection feature. It's similar to grouping but for temporary groups.

We just need to specify the id of the features to select:

// uncomment in index.maplibre.dev.ts
geoman.features.setSelection([1, 2, 3, 4, 5, 6, 7, 151]);

Translations, rotations, and deletions will be grouped if we edit one of these features; otherwise the selection is cleared. I see you have a lasso pro feature, which I think could be interesting? Alternatively, we could implement our own home-made selection method to connect to geoman.

selection.prepared.mp4

@mscno
Copy link
Contributor

mscno commented Dec 17, 2025

Thanks a lot for the work on this one. I will be away for the next week or so, but will go through it in details when I find the time.

I unfortunately broke the tests with the dev panel, i also made a fix for this to disable dev panel in tests in another branch, but great you caught it as well.

@lhapaipai
Copy link
Contributor Author

Hi! I've rebased the source code to resolve conflicts.
Open to discussion :-)

You can find a usage example using a selection box.

selection.prepared.mp4

@zxwild
Copy link
Contributor

zxwild commented Mar 3, 2026

I'm trying to align the current master with the PR, the branch is here:
https://github.com/geoman-io/maplibre-geoman/tree/feature/group-features

Feel free to take the changes to your PR if they are suitable :)

I'm not sure I understand completely the full code, so I'll post questions and offers, the first:

Some draw modes has features.clearSelection(), some not, for example: draw:polygon.
Could it be done in BaseDraw.onStartAction for all shapes at once?

@lhapaipai lhapaipai force-pushed the f/#90-group-features branch from 1418ce5 to a9109e4 Compare March 3, 2026 17:05
@lhapaipai
Copy link
Contributor Author

Thanks a lot @zxwild for taking the time to fix the merge conflicts!
I was actually planning to do it this morning!

Of course we can start discussing the feature.

I added the clearSelection() method when the user begins drawing a new feature, not when the new mode is activated (that's why I didn't write the code in onStartAction).

Normally all draw modes call features.clearSelection(). There is an exception for the line and polygon draw modes which use the LineDrawer. In that case, the method is invoked in the onMouseClick method of the LineDrawer.

this.gm.features.clearSelection();

@zxwild zxwild merged commit 6a0695a into geoman-io:master Mar 4, 2026
7 checks passed
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.

Allow grouping Features when moving, rotating

3 participants