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

Compatibility with Leaflet.PolylineDecorator #20

Closed
n3d1117 opened this issue Mar 19, 2019 · 22 comments
Closed

Compatibility with Leaflet.PolylineDecorator #20

n3d1117 opened this issue Mar 19, 2019 · 22 comments

Comments

@n3d1117
Copy link

n3d1117 commented Mar 19, 2019

Hello!

I'm trying to draw an arrow in the middle of a L.Curve, using Leaflet.PolylineDecorator library.
What I'd like to do is:

var curve = L.Curve(...).addTo(map);
var arrowDecorator = L.polylineDecorator(curve, {
    patterns: [
       // arrow at 50% of the path
       {offset: '50%', repeat: 0, symbol: L.Symbol.arrowHead({pixelSize: 15, polygon: false, pathOptions: {stroke: true}})}
    ]
}).addTo(map);

However PolylineDecorator only accepts L.Polyline, L.Polygon or an array of L.LatLng as a parameter.
Is there maybe any way to extract an array of latlngs from the curve and use that?

Thanks!

@elfalem
Copy link
Owner

elfalem commented Mar 20, 2019

Hello,

Unfortunately I don't think this is possible. Bezier curves are defined by a small number of latlngs that indicate key points and from which the rest of the curve is derived. If the list of these points is supplied to PolylineDecorator, it likely will try to decorate a straight line between the points not following the curve which I don't think is desired.

I'm open to suggestions if anyone has possible solutions.

Thanks

@n3d1117
Copy link
Author

n3d1117 commented Mar 20, 2019

Hello,

I didn't mean to use the key points: what I meant with

array of latlngs from the curve

was to maybe extract an approximation of the curve using a finite number of points (say 100) and provide that to PolylineDecorator. I don't know if that would be possible.

Thanks for your help!

@elfalem
Copy link
Owner

elfalem commented Mar 21, 2019

OK, thanks for the clarification. I'll try to find an algorithm that does that.

@khetamHamad
Copy link

Any update or advice related to this?

Thanks!

@elfalem
Copy link
Owner

elfalem commented Apr 7, 2019

I'm still looking for an algorithm for sampling the points.

elfalem added a commit that referenced this issue Apr 11, 2019
@elfalem
Copy link
Owner

elfalem commented Apr 11, 2019

@n3d1117 @khetamHamad I have implemented a new function trace() that provides an array of points that are on the curve. The points can then be supplied to PolylineDecorator.

Example usage in the context of the demo page:

var points = pathFour.trace([0, 0.25, 0.5, 0.75, 1])
points.forEach(i => L.circle(i, {color: 'green'}).addTo(map));

trace() takes an array of sample distances. These distances will be applied to each segment (i.e. command) of the curve. Valid distance values are between 0 and 1 inclusive. 0 signifies the start point of a segment and 1 signifies the end point. For a straight line segment, 0.5 would represent the halfway point. (note that for curves, 0.5 would be close to but not exactly the halfway point).

The changes are on the master branch. Please let me know if you have any feedback. I intended to release a new version soon.

@khetamHamad
Copy link

khetamHamad commented Apr 11, 2019

First of all, thank you elfalem for helping me in this.
I downloaded the new leaflet.curve.js and tried to add the above lines of code to my code (below), but i had an error
jquery-3.3.1.js:3827 Uncaught TypeError: Cannot read property 'length' of undefined
at e.trace (index.html?number=&pod=AlAlawi:33548)
at HTMLDocument. (index.html?number=&pod=AlAlawi:34541)
at mightThrow (jquery-3.3.1.js:3534)
at process (jquery-3.3.1.js:3602), any advice to resolve it?

var style = {weight:weig, opacity:weig, color: col , animate: {duration: 3000, delay: 1000}};
var curvedPath = L.curve(
[
M', latlng1,
'Q', midpointLatLng,
latlng2
], style);

L.polylineDecorator(curvedPath,
{
patterns: [
{offset:"50%", repeat: 0, symbol: new L.Symbol.ArrowHead({pixelSize:size, polygon: false, pathOptions: {weight:weig ,color:arrowColor, stroke: true}})}
]
}).bindPopup(details).addTo(paths);
curvedPath.bindPopup(details).addTo(paths);

@elfalem
Copy link
Owner

elfalem commented Apr 11, 2019

This is because polylineDecorator doesn't recognize curvedPath. Instead of supplying the path itself, obtain an array of points using the trace function and provide that to the polylineDecorator.

In your code, L.polylineDecorator(curvedPath, {... would change to L.polylineDecorator(curvedPath.trace([0, 0.25, 0.5, 0.75, 1]), {...

@n3d1117
Copy link
Author

n3d1117 commented Apr 11, 2019

Thanks @elfalem for your time. Although I ended up using a different workaround, this really looks like a great addition!

@elfalem
Copy link
Owner

elfalem commented Apr 16, 2019

A new release has been published.

@elfalem elfalem closed this as completed Apr 16, 2019
@khetamHamad
Copy link

Hello elfalem,

Sorry for re-open this issue but I used L.polylineDecorator(curvedPath.trace([0, 0.25, 0.5, 0.75, 1]),...) and have an error related to "Uncaught TypeError: Cannot read property 'length' of undefined"
any advice related to this issue or the version of leaflet and jquery.

Best regards.

@elfalem
Copy link
Owner

elfalem commented May 1, 2019

@khetamHamad This is probably the same issue as #23. Version 0.5.2 should fix it.

@khetamHamad
Copy link

khetamHamad commented May 1, 2019

Hello,
after i updated the library from 0.5 to 0.5.2 i still face the same issue "jquery-3.4.0.min.js:2 Uncaught TypeError: Cannot read property 'length' of undefined"
The following is my code
var weig = sizeFun(num, max, min) -2;
var style = {weight:weig, opacity:weig-0.5, color:'#228B22', animate: {duration: 3000, delay: 1000}};
var curvedPath = L.curve(
[
'M', latlng1,
'Q', midpointLatLng,
latlng2
], style);
var lines = new L.Polyline([[ flow[j].f_lat,flow[j].f_long], [flow[j].t_lat, flow[j].t_long]], style);
L.polylineDecorator(curvedPath.trace([0,0.5,1]),
{
patterns: [
{offset:"50%", repeat: 0, symbol: new L.Symbol.ArrowHead({pixelSize:size, polygon: false, pathOptions: {weight:weig ,color:arrowColor, stroke: true}})}
]
}).bindPopup(details).addTo(paths);
curvedPath.bindPopup(details).addTo(paths);

it always complains in the following line
trace: function(t){
t = t.filter(function(element){
return element >= 0 && element <= 1;
});

var point, curCommand, curStartPoint, curEndPoint;
var p1, p2, p3;
var samples = [];
for(var i = 0; i < this._points.length; i++){

@elfalem
Copy link
Owner

elfalem commented May 3, 2019

@khetamHamad From the code snippet, you've provided it sounds like it's related to #22. trace() only works after the curve has been added to the map. If paths is the map, it looks like trace() is being called before the curve has been added to the map. One way to fix could be moving the last line in the snippet before polylineDecorator:

...
var lines = new L.Polyline([[ flow[j].f_lat,flow[j].f_long], [flow[j].t_lat, flow[j].t_long]], style);
curvedPath.bindPopup(details).addTo(paths);
L.polylineDecorator(curvedPath.trace([0,0.5,1]),
...

@khetamHamad
Copy link

The path is a groupedLayers and it is added by default in loading the map.

Best.

@elfalem
Copy link
Owner

elfalem commented May 5, 2019

In that case I'm not sure I understand the code snippet you provided. Are you able to provide a minimal but complete example that illustrates the issue?

@aasilva
Copy link

aasilva commented Oct 20, 2022

Hello @elfalem
First of all, thanks for your incredible work.

I'm having an undesired behavior when adding an arrowHead to a Curve. Depending on the zoom level, the position of the arrowHead is a bit off the curve.

I'm using the following, code based on the Demo:

var curvedPath = L.curve([
  "M",
  [46.86019101567027, -29.047851562500004],
  "Q",
  [50.48547354578499, -23.818359375000004],
  [46.70973594407157, -19.907226562500004],
  "T",
  [46.6795944656402, -11.0302734375]
]).addTo(map);

const arrowHead = L.polylineDecorator(curvedPath.trace([0, 0.25, 0.5, 0.75, 1]), {
  patterns: [
    {
      offset: "50%",
      repeat: 0,
      symbol: L.Symbol.arrowHead({
        pixelSize: 15,
        polygon: false,
        pathOptions: { stroke: true }
      })
    }
  ]
}).addTo(map);

You can also see this behavior on the demo page (http://elfalem.github.io/Leaflet.curve/). If you zoom out and then click on the Trace Curves button, you'll see that the circles are not exactly on top of the curve.

Is there any solution to this?

Thank you,
André

@elfalem
Copy link
Owner

elfalem commented Oct 21, 2022

Hello André,
How much zooming out is necessary to exhibit the behavior? As far as I can tell, the circles tend to be on top of the curve. Can you provide a picture of the issue?

@aasilva
Copy link

aasilva commented Oct 21, 2022

Here's the use case:

Zoom out to zoom level 3 and click on Trace Curves
leaflet-zoom-3

Then start to zoom in; you'll see that after a certain zoom level, the deviation starts to be perceptive.
See the images below:

leaflet-zoom-9
leaflet-zoom-12

@elfalem
Copy link
Owner

elfalem commented Oct 22, 2022

Thanks for the detailed steps. I'm guessing the issue is related to precision of the points because it seems to occur only when zooming in after tracing. Tracing the curves when already zoomed shows correct positions.

I'm not sure how easy or difficult it is to resolve this. But I'll investigate (any assistance is appreciated). In the meantime, one workaround is to obtain the trace points when zoomed in, although I'm not sure how practical that is for your use case.

@aasilva
Copy link

aasilva commented Oct 24, 2022

I will also try to investigate and let you know if I find something. Unfortunately, getting the trace points on a higher zoom level is not an option for me. Thanks for your attention.

@elfalem
Copy link
Owner

elfalem commented Nov 30, 2022

So I have been looking at this for a while. It seems like the only possible approach that could work is computing the trace points at the highest zoom level.

I've been looking into how to do that regardless of the zoom level at which the trace() command is invoked. There is a useful method map.project(<LatLng> latlng, <Number> zoom) that looks promising. However, the obstacle is that I also would need to obtain the pixel origin at the same zoom level. There is map.getPixelOrigin() but it doesn't take zoom level as a parameter.

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

No branches or pull requests

4 participants