openlayers / openlayers

OpenLayers
https://openlayers.org
BSD 2-Clause "Simplified" License
11.3k stars 3.03k forks source link

OL native Geodesic circle type #12859

Closed jt-helsinki closed 2 years ago

jt-helsinki commented 2 years ago

Is your feature request related to a problem? Please describe.

Creating and using geodesic circles is too difficult in open layers. It's probably not such an uncommon use case and it would be very handy to have this easily used with the OL Interactions without having to write custom geometry/style functions.

Describe the solution you'd like

Create an OL native GeodesicCircle type which can be used like this:

const circle = new GeodesicCircle(radius, coordinate, number_of_sides)
...
circle.radius()
circle.center()
...
TDesjardins commented 2 years ago

Looks for me like the existing Polygon.circular-function: https://github.com/openlayers/openlayers/blob/v6.8.1/src/ol/geom/Polygon.js#L426

jt-helsinki commented 2 years ago

Kind of but not really. Unless I am mistaken, this function just builds a polygon with n sides. It's just a representation of a circle so if you try to resize, only the point which you grab is moved. The rest of the "circle" stays put. Moreover, as this is just a polygon, there is no radius or centre coordinate attached to this circular polygon.

Check out this example:

https://openlayers.org/en/latest/examples/draw-and-modify-geodesic.html

There's quite a bit involved with getting the radius, rescaling and dragging. Ideally, this functionality would be wrapped up inside the GeodesicCircle class.

tschaub commented 2 years ago

I agree that there is a lot of code in that example.

Here is an example that is somewhat simpler: https://codesandbox.io/s/geodesic-circle-4rdwq?file=/main.js

The idea with the above example is to use a feature with a point geometry to represent a circle:

const defaultRadius = 500 * 1e3; // in meters

const feature = new Feature({
  radius: defaultRadius,
  geometry: new Point([0, 0]),
});

Then, instead of rendering it as a point, you render it as a geodesic circle:

const layer = new VectorLayer({
  source: source,
  style: function (feature) {
    const center = feature.getGeometry().clone();
    center.transform('EPSG:3857', 'EPSG:4326');

    const circle = circular(center.getCoordinates(), feature.get('radius'));
    circle.transform('EPSG:4326', 'EPSG:3857');

    return new Style({
      geometry: circle,
      fill: new Fill({
        color: 'rgba(0, 60, 136, 0.5)',
      }),
    });
  },
})

The back and forth with the transform functions is an unfortunate complexity. But this is the same reason we don't have a geodesic circle geometry type in the library. Aside from that custom style function, you don't need to do anything special to get the Translate interaction to work.

In OpenLayers, geometries know nothing about their spatial reference system. A geometry is represented by coordinate values, but they don't keep track of whether those coordinates represent longitude/latitude pairs or coordinates in any one of thousands of projected coordinate reference systems.

The map view, by contrast, does know about a spatial reference system. By default, views are rendered in the Web Mercator projection (EPSG:3857).

When you create a polygon representing a geodesic circle, you provide a center and radius. The circular function exported by the ol/geom/Polygon.js module only works if you use a radius in meters and a center in geographic coordinates. This means that it needs to be transformed before being rendered on a non-geographic view.

If we added a GeodesicCircle geometry constructor to the library, it would also need to take a projection property. This would make it different than all of the other current geometry types that don't know anything about a projection or spatial reference. Given that it isn't really all that hard to make an example that does something like render a geodesic circle and allow dragging it around, I'm not sure there will be much motivation to add yet another geometry type and have it be so different from the others.

We have discussed making changes so that the whole OpenLayers API works in geographic coordinates (or potentially any other user-configured projection). This may require making it so geometries (and extents etc.) are always passed around with a spatial reference system identifier. If we did this, it might also make sense to repurpose the current Circle geometry type to be a geodesic circle at the same time.

stale[bot] commented 2 years ago

This issue has been automatically marked as stale because it has not had recent activity. It will be closed if no further activity occurs. Thank you for your contributions.