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

Fixed displacing points specified from context menu #4904

Conversation

nenad-vujicic
Copy link
Contributor

@nenad-vujicic nenad-vujicic commented Jun 17, 2024

This PR addresses "Directions from here often places the point hundreds of meters elsewhere" issue mentioned in #2982

Fixes #2982 by updating directions.js so it doesn't perform unnecessary geocoding anymore for previously selected point (either source or destination), when entering second point. Also, improved setting markers by positioning it to selected position (not to result of geocoding).

@tomhughes
Copy link
Member

How does it "address" that issue? What is the change you are making to address it?

@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch 2 times, most recently from 77a8796 to 7afb4ce Compare June 17, 2024 14:55
@nenad-vujicic
Copy link
Contributor Author

Dear @tomhughes ,

Thank you for your review and comment. I updated PR and commit messages. Please, let me know if I can improve it further.

Thanks,
Nenad.

@AntonKhorev
Copy link
Collaborator

so it doesn't perform unnecessary geocoding anymore for the previously selected point (either source or destination), when entering second point

Doesn't it still do unnecessary geocoding for the first point? And then its marker may get shifted as a result of this geocoding.

What an endpoint should do is to detect that it has coordinates as a value and don't try to query nominatim if it does. Search has server-side regexps to detect coordinates:

if latlon = query.match(/^([NS])\s*(\d{1,3}(\.\d*)?)\W*([EW])\s*(\d{1,3}(\.\d*)?)$/).try(:captures) || # [NSEW] decimal degrees

Here something simpler might suffice like number, number format without all those degree signs, N/S/E/W letters etc.

@AntonKhorev
Copy link
Collaborator

related #1754

@nenad-vujicic nenad-vujicic marked this pull request as draft June 25, 2024 16:42
@nenad-vujicic nenad-vujicic marked this pull request as draft June 25, 2024 16:42
@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from 7afb4ce to b0b3ecd Compare July 2, 2024 12:58
@nenad-vujicic
Copy link
Contributor Author

nenad-vujicic commented Jul 2, 2024

Dear @AntonKhorev ,

I've just updated PR with the latest, simplified and corrected sources. I made short video displaying new functionality (I added debugging alert("GEOCODING") call to directions.js just before $.getJSON(OSM.NOMINATIM_URL .. line to catch geocoding requests).

Screen.Recording.2024-07-02.144007.mp4

Now, geocoding is called only once when user selects "Directions from here" or "Directions to there" or when new address is selected. Another way is to call geocoding only when address is typed, but, then, address will be lost when context menu options are selected (like when dragging markers, i.e. in From / To fields will be drawn Lat / Lng only).

Please, let me know which one option is ideal or if it could be further improved?

Thanks,
Nenad.

@nenad-vujicic nenad-vujicic marked this pull request as ready for review July 2, 2024 13:29
Comment on lines -130 to -131
var precision = OSM.zoomPrecision(map.getZoom());
input.val(ll.lat.toFixed(precision) + ", " + ll.lng.toFixed(precision));
Copy link
Collaborator

Choose a reason for hiding this comment

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

This code was used by $("#map").on("drop" ... to populate an input when the marker next to the input is dragged and dropped on the map.

What happens now:
Screencast from 04.07.2024 06:35:02.webm

What used to happen:
Screencast from 04.07.2024 06:38:26.webm

@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from b0b3ecd to 8aa3ff8 Compare July 4, 2024 07:43
@nenad-vujicic
Copy link
Contributor Author

Dear @AntonKhorev ,

Good catch, thank you very much! I've just fixed it and now it works fine! Please, let me know if I could further improve it.

Thanks,
Nenad.

@AntonKhorev
Copy link
Collaborator

Going to Nominatim with coordinates still happens. Try entering "0,0" into from/to inputs.

Before the changes the code always went to Nominatim and searched for these numbers, found them somewhere in Taiwan (or elsewhere depending on the current map view) and placed the marker there.

After the changes the same happens except the marker is at 0, 0.

We should

  • either decide that we're not searching for input values when they match the coordinate format,
  • or never populate inputs with coordinates (and maybe populate them with reverse geocoding results).

@nenad-vujicic nenad-vujicic marked this pull request as draft July 8, 2024 09:28
@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from 8aa3ff8 to afc0f21 Compare July 8, 2024 21:01
@nenad-vujicic
Copy link
Contributor Author

nenad-vujicic commented Jul 8, 2024

Dear @AntonKhorev ,

Thanks for reviewing and comments. Now, we are contacting nominatim only if: point is defined using context menu (right click + Directions from/to here) or something is entered that doesn't look like coordinates (Number "," or "/" Number). Also, we improved focusing of map view, so it focuses on union of markers and path (if exists), previously it focused only on path.

Please, let me know if we could further improve it.

Thanks,
Nenad.

@nenad-vujicic nenad-vujicic marked this pull request as ready for review July 8, 2024 21:26
@AntonKhorev
Copy link
Collaborator

AntonKhorev commented Jul 18, 2024

Another bug:

  1. open the directions panel
  2. select directions from here from the context menu
simplescreenrecorder-2024-07-18_17.44.10.mp4

@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch 2 times, most recently from 98acb86 to eb504ae Compare July 19, 2024 10:38
@nenad-vujicic
Copy link
Contributor Author

nenad-vujicic commented Jul 19, 2024

Dear @AntonKhorev ,

Thank you for catching this! I've just fixed it. Please, let me know if I could further improve it.

Thanks,
Nenad.

@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch 2 times, most recently from 5b2e432 to 6489d0e Compare July 20, 2024 14:03
});

endpoint.setValue = function (value, latlng) {
endpoint.setValue = function (value, force_reverse_geocoding, latlng) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What is force_reverse_geocoding? Is there any code here that actually does reverse geocoding?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, there is. Reverse geocoding is done when user right-clicks on map and selects "Directions from here" or "Directions to here".

Variable force_reverse_geocoding is introduced to pass information that user did this (right clicked and selected from context menu) and to perform reverse geocoding.

Copy link
Collaborator

Choose a reason for hiding this comment

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

There's no forcing of reverse geocoding. There's no code calling reverse like here. Nominatim will usually do reverse geocoding when "Directions from/to here" are selected, but that's because it decides on its own that the input query looks like coordinates. It won't make this decision correctly every time.

simplescreenrecorder-2024-07-22_01.46.00.mp4

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this! I've just updated with adding code for calling "reverse" like in above link.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks for catching this! I've just updated with adding code for calling "reverse" like in above link.

We have just gotten another idea how to improve this part. I'll make another push soon.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

@AntonKhorev I've just pushed the latest fixes.

We improved logic of what is displayed in input boxes after right-clicking and selecting option from context menu. Now, if nominatim doesn't return anything or it returns something that is more than 200m away from clicked position, we will display coordinates (Number, Number), otherwise we will display what nominatim returned. The logic behind is "in input boxes should be displayed user-friendly text iff user entered it manually or user clicked on / or near the object recognized by nominatim, otherwise, coordinates will be displayed".

For calculating distance between position where user clicked and position of what nominatim returned, we used distanceTo method provided by Leaflet's LatLng which reduces it to calculating distance on great circle. After testing on Longyearbyen and Longyearbyen, it seems this approximation works fine.

Please, let me know if we can further improve it.

Thanks,
Nenad.

@@ -402,8 +440,10 @@ OSM.Directions = function (map) {
}
}

endpoints[0].setValue(params.from || "", from);
endpoints[1].setValue(params.to || "", to);
var ll_from_search_form = decodeURIComponent(location.search).match(/^\?from=([+-]?\d+(\.\d*)?)(?:\s+|\s*[/,]\s*)([+-]?\d+(\.\d*)?)$/);
Copy link
Collaborator

Choose a reason for hiding this comment

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

Does this regexp ever match anything?

"Directions from here" generate location.search like ?from=52.54124,13.56290&to= which doesn't match. Do you want it to match?

Copy link
Contributor Author

@nenad-vujicic nenad-vujicic Jul 20, 2024

Choose a reason for hiding this comment

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

Yes, it does. It captures the situation when user inputs some coordinates into search field and then pushes "Find directions between two points" button. Earlier, this performed reverse geocoding, now it doesn't do it anymore.

Our idea behind the current solution is "if user enters the coordinates or drags markers, leave the coordinates. If user right-clicks and selects from context menu, perform reverse geocoding".

@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from 6489d0e to 139d6c4 Compare July 24, 2024 10:07
@nenad-vujicic nenad-vujicic marked this pull request as draft July 25, 2024 09:57
@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from 139d6c4 to 4394f80 Compare July 25, 2024 12:07
@nenad-vujicic nenad-vujicic marked this pull request as ready for review July 26, 2024 10:38
@nertc
Copy link
Contributor

nertc commented Aug 6, 2024

When user tries to add a marker out of the borders of countries, alert is thrown "Sorry - couldn't locate", but marker is still added and if user drags it to the valid position, route is not updated (see Video 1).

Video 1:

OpenStreetMap.and.45.more.pages.-.Work.-.Microsoft.Edge.2024-08-06.13-37-54.mp4

Contrary to this, if user adds marker in the valid location and then drags it out of the borders, route is still calculated and shown without any alerts (see Video 2).

Video 2:

OpenStreetMap.and.45.more.pages.-.Work.-.Microsoft.Edge.2024-08-06.13-38-13.mp4

Maybe, in the first case, we should still show coordinates and calculate route even though Nominatim location was not found (as it is done in the second case).

@nertc
Copy link
Contributor

nertc commented Aug 6, 2024

The same logic is with "Couldn't find a route between those two places" error. If it is thrown, dragging location pin doesn't update route (see Video).

Video:

OpenStreetMap.and.45.more.pages.-.Work.-.Microsoft.Edge.2024-08-06.13-53-27.mp4

alert(I18n.t("javascripts.directions.errors.no_place", { place: endpoint.value }));
return;
}
if (endpoint.value.match(/^([+-]?\d+(\.\d*)?)(?:\s+|\s*[/,]\s*)([+-]?\d+(\.\d*)?)$/)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

As we reuse /^([+-]?\d+(\.\d*)?)(?:\s+|\s*[/,]\s*)([+-]?\d+(\.\d*)?)$/ regex, it would be more understandable if we create separate constant variable for it and then use it with the variable name.

geocodeCallback();
});
} else {
$.getJSON(OSM.NOMINATIM_URL + "search?q=" + encodeURIComponent(endpoint.value) + "&format=json&viewbox=" + viewbox, function (json) {
Copy link
Contributor

Choose a reason for hiding this comment

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

When concatenating many strings, I think, using Template literals are more suitable and easier to read way.

$.getJSON(OSM.NOMINATIM_URL + "search?q=" + encodeURIComponent(endpoint.value) + "&format=json&viewbox=" + viewbox, function (json) {
endpoint.awaitingGeocode = false;
endpoint.hasGeocode = true;
if (!json || json.length === 0) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This can be shortened as !json?.length

Copy link
Collaborator

Choose a reason for hiding this comment

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

let/const, template literals, ?. etc - we should decide on these in a separate issue, not in this PR.

Copy link
Contributor

Choose a reason for hiding this comment

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

If it needs a separate issue, we can create it, however I didn't mean refactoring of the old code (which definitely would be a separate issue), but to change them in the new code. Also, if somewhere we have coding style guidelines, please, link it and I'll create an issue linked to it.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Looked at what is already in use by included libraries:

  • let/const - used by Bootstrap and Turbo
  • ?. - used by Turbo
  • template literals - used by Bootstrap and Turbo

@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from 4394f80 to 2e20a63 Compare August 7, 2024 14:51
Copy link
Contributor

@nertc nertc left a comment

Choose a reason for hiding this comment

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

Everything I tested, worked perfectly. This is just recommendation for less code to be written.

@@ -5,6 +5,7 @@
OSM.Directions = function (map) {
var awaitingRoute; // true if we've asked the engine for a route and are waiting to hear back
var chosenEngine;
var coordinatesRegularExpression = "([+-]?\\d+(\\.\\d*)?)(?:\\s+|\\s*[/,]\\s*)([+-]?\\d+(\\.\\d*)?)";
Copy link
Contributor

Choose a reason for hiding this comment

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

To avoid new RegExp("^" + coordinatesRegularExpression + "$") every time we use this variable, you can define it as var coordinatesRegularExpression = /^([+-]?\d+(\.\d*)?)(?:\s+|\s*[\/,]\s*)([+-]?\d+(\.\d*)?)$/; and it will automatically be regexp.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why does this expression have capture groups if they are not used? Instead there's endpoint.value.split(/[/,]/).

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, I agree, I could have used better solutions on many places. My intention was to fix the bug and to make it simple, short and readable as much as I can. The source code is already a little bit complex and requires refactoring. We can optimize it in some other PR.

@nenad-vujicic
Copy link
Contributor Author

All above is fixed and PR is again ready to be reviewed. Thanks!

@@ -51,7 +43,7 @@ OSM.Directions = function (map) {
select.append("<option value='" + i + "'>" + I18n.t("javascripts.directions.engines." + engine.id) + "</option>");
});

function Endpoint(input, iconUrl, dragCallback, geocodeCallback) {
function Endpoint(input, iconUrl) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

You're undoing #4902. I was going to move endpoint code into a separate module because it's difficult to test this one.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I apologize for the undoing, I thought it's just simplifying .. I returned using callback for calling getRoute, so, Endpoint can be pulled out now to separate module.

Thanks,
Nenad.

Fixes openstreetmap#2982 by updating directions.js so it doesn't perform unnecessary geocoding anymore for previously selected point (either source or destination) when entering second point. Allows reverse geocoding only when coordinates are specified from context menu. Also, improved setting markers by positioning it to selected position (not to result of geocoding) and improved focusing map view to include markers.
@nenad-vujicic nenad-vujicic force-pushed the issue_2982_displacing_point_away branch from 5b6e81e to f9d39a5 Compare August 8, 2024 19:34
});

endpoint.setValue = function (value, latlng) {
endpoint.setValue = function (value, force_reverse_geocoding, latlng, fitMap) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Why would a single endpoint control fitting?

Then below you have:

endpoints[0].setValue(params.from || "", !ll_from_search_form, from, false);
endpoints[1].setValue(params.to || "", !ll_from_search_form, to, true);

And not it's not clear why the first endpoint gets fitMap = false but the second one gets fitMap = true.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Why would a single endpoint control fitting?

Because the route can change on single endpoint update, so, it can go out of viewing area and we need to fit bounds.

Then below you have:

endpoints[0].setValue(params.from || "", !ll_from_search_form, from, false);
endpoints[1].setValue(params.to || "", !ll_from_search_form, to, true);

And not it's not clear why the first endpoint gets fitMap = false but the second one gets fitMap = true.

Because in some situations there is a flickering if we are doing fitting both times.

Copy link
Collaborator

Choose a reason for hiding this comment

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

Why would a single endpoint control fitting?

Because the route can change on single endpoint update

It's for the code outside of endpoints to determine what to do with the route.

return;
}

if (endpoint.latlng.distanceTo(L.latLng(json.lat, json.lon)) > 200.0) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

What was this distance check about? Was it to filter out useless reverse geocoding results that are too far away from the endpoint?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, exactly! If it is found something near (e.g. 200m) the clicked coordinates, accept it, otherwise, display coordinates.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't do this in #5064 because it's a different problem, but it probably should be solved somewhere.

What's "near" probably depends on the current zoom level if the coordinates came from a click on the map.

@nenad-vujicic
Copy link
Contributor Author

Closed in favor of #5064

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.

"Directions from here" often places the point hundreds of meters elsewhere
4 participants