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

[Bug] Geodesic circles can be drawn but not moved or resized #436

Open
neodescis opened this issue Jan 22, 2025 · 2 comments
Open

[Bug] Geodesic circles can be drawn but not moved or resized #436

neodescis opened this issue Jan 22, 2025 · 2 comments
Labels
bug Something isn't working

Comments

@neodescis
Copy link

neodescis commented Jan 22, 2025

Describe the bug
TerraDrawCircleMode can be configured to create geodesic circles (projection: globe). However, once the circle is created, it cannot be moved or resized via TerraDrawSelectMode such that it stays geodesic. The documentation alludes to this, but this seems like something that needs to be fixed for geodesic circles to be useful.

Terra Draw npm version
1.0.0

To Reproduce
Steps to reproduce the behavior:

  1. Enter TerraDrawCircleMode with projection: globe
  2. Draw a geodesic circle close to the north pole
  3. Enter TerraDrawSelectMode with projection: globe
  4. Attempt to resize the circle
  5. See console errors about resize not being supported
  6. Move the circle close to the equator
  7. The circle gets "squished" into a shape that cannot be drawn with the circle tool. So, either it was drawn with an incorrect projection originally, or it is getting translated incorrectly as it is dragged. I suspect the latter, but I have not confirmed this.

Expected behavior
The circle should be continuously re-shaped to the correct projection as it is moved or resized.

@neodescis neodescis added the bug Something isn't working label Jan 22, 2025
@neodescis neodescis changed the title [Bug] Geodesic circles can be drawn but not moved or resized [Bug] Geodesic circles can be drawn but not resized Jan 22, 2025
@neodescis neodescis changed the title [Bug] Geodesic circles can be drawn but not resized [Bug] Geodesic circles can be drawn but not moved or resized Jan 22, 2025
@neodescis
Copy link
Author

I would also expect the radiusKilometers property to be updated when the shape is resized. I'm thinking maybe circles should have special logic for updating them in the select mode? If that is the right path forward, we might also want to store the center point in properties as well.

@neodescis
Copy link
Author

neodescis commented Jan 23, 2025

Actually, I have figured out how to override the select mode's featureDrag and dragCoordinateResizeFeature behaviors to recreate the circle on the fly. I didn't even have to rely on the saved radiusKilometers or a center property, as turf can figure these things out from the points. This is ugly, but it gets the job done! @JamesLMilner, can we add a special case like this for circles to select mode? If so, I'd be happy to create a pull request.

const originalRegisterBehaviors = mode.registerBehaviors.bind(mode);
mode.registerBehaviors = (config: unknown) => {
  originalRegisterBehaviors(config);

  // Update feature dragging to keep geodesic circles
  (() => {
    let originalCircle: { id: NonNullable<Feature['id']>, center: Position, radius: number } | null = null;
    let dragStart: Position | null = null;
    const originalStartDragging = mode.dragFeature.startDragging.bind(mode.dragFeature);
    const originalDrag = mode.dragFeature.drag.bind(mode.dragFeature);

    mode.dragFeature.startDragging = (event: TerraDrawMouseEvent, id: NonNullable<Feature['id']>) => {
      const feature = terraDraw.getSnapshot().find((f) => f.id === id);
      if (feature?.properties['mode'] === 'circle' && feature?.geometry.type === 'Polygon') {
        const center = turfCenter(feature).geometry.coordinates;
        const radius = turfDistance(center, feature.geometry.coordinates[0][0]);
        originalCircle = { id, center, radius };
        dragStart = [event.lng, event.lat];
      } else {
        originalCircle = null;
      }
      originalStartDragging(event, id);
    };

    mode.dragFeature.drag = (event: TerraDrawMouseEvent, validateFeature?: unknown) => {
      const { selectionPoints, midPoints, store } = mode.dragFeature;
      if (originalCircle && dragStart) {
        const cursorCoord = [event.lng, event.lat];
        const delta = [
          dragStart[0] - cursorCoord[0],
          dragStart[1] - cursorCoord[1]
        ];
        const newCenter = [
          originalCircle.center[0] - delta[0],
          originalCircle.center[1] - delta[1]
        ];
        const newGeometry = turfCircle(newCenter, originalCircle.radius).geometry;
        const updatedSelectionPoints = selectionPoints.getUpdated(newGeometry.coordinates[0]) || [];
        const updatedMidPoints = midPoints.getUpdated(newGeometry.coordinates[0]) || [];
        store.updateGeometry([
          { id: originalCircle.id, geometry: newGeometry },
          ...updatedSelectionPoints,
          ...updatedMidPoints
        ]);
      } else {
        originalDrag(event, validateFeature);
      }
    };
  })();

  // Update coordinate resize dragging to keep geodesic circles
  (() => {
    let originalCircle: { id: NonNullable<Feature['id']>, center: Position, radius: number } | null = null;
    const originalStartDragging = mode.dragCoordinateResizeFeature.startDragging
      .bind(mode.dragCoordinateResizeFeature);
    const originalDrag = mode.dragCoordinateResizeFeature.drag
      .bind(mode.dragCoordinateResizeFeature);

    mode.dragCoordinateResizeFeature.startDragging = (id: NonNullable<Feature['id']>, coordinateIndex: number) => {
      const feature = terraDraw.getSnapshot().find((f) => f.id === id);
      if (feature?.properties['mode'] === 'circle' && feature?.geometry.type === 'Polygon') {
        const center = turfCenter(feature).geometry.coordinates;
        const radius = turfDistance(center, feature.geometry.coordinates[0][0]);
        originalCircle = { id, center, radius };
      } else {
        originalCircle = null;
      }
      originalStartDragging(id, coordinateIndex);
    };

    mode.dragCoordinateResizeFeature.drag = (
      event: TerraDrawMouseEvent,
      resizeOption?: unknown,
      validateFeature?: unknown
    ) => {
      const { selectionPoints, midPoints, store } = mode.dragCoordinateResizeFeature;
      if (originalCircle) {
        const cursorCoord = [event.lng, event.lat];
        const newRadius = turfDistance(originalCircle.center, cursorCoord);
        const newGeometry = turfCircle(originalCircle.center, newRadius).geometry;
        const updatedSelectionPoints = selectionPoints.getUpdated(newGeometry.coordinates[0]) || [];
        const updatedMidPoints = midPoints.getUpdated(newGeometry.coordinates[0]) || [];
        store.updateGeometry([
          { id: originalCircle.id, geometry: newGeometry },
          ...updatedSelectionPoints,
          ...updatedMidPoints
        ]);
      } else {
        originalDrag(event, resizeOption, validateFeature);
      }
    };
  })();
};

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

1 participant