⬆️ ⬇️

Finding the minimum distance to a curve using the Yandex.Maps API

Hello dear readers.



If you have ever encountered the task described in the topic, you are probably surprised that there will be anything in the post other than a link to the description of the getClosestPoint () method, so I’ll say right away that my decision is of course based on it. However, I want to share not so much the algorithmic beauty (the Yandex team implemented it for me by creating an API), as a ready-made solution to the task set before me.



Our company is engaged in including the presentation of IP-telephony services and the Internet, as well as kilometers of its own optical fiber. The question of how far from the cable the office of the prospective client is located is very important for us.

')

A ready-made solution, from creating a map with optics routes, to implementing the shortest distance definition embedded in the forms, look under the cut.



I will not deceive neither you nor myself trying to hide that all the work was essentially a compilation of ready-made solutions and examples, I want to thank at the beginning all those whose insights helped me to implement the project entirely.



The task is as follows: the user enters the address



Or he chooses the house by poking the mouse at the desired point.



After that, a label is placed on the map with the selected address and distance to the nearest optical cable. For convenience of display, a perpendicular is drawn to the cable (controversial decision, but I visually liked the result).



I give an example that would not be unfounded http://www.infotis.ru/maps.html .



The work has been reduced to several stages.



  1. Transfer cables from a physical card to Yandex.Maps
  2. Put the cables on the card in the form of curves (polylines)
  3. Create custom controls
  4. Write a function calculating distance
  5. Get the desired address and on it the necessary coordinates




Stage 1



At this stage, it was necessary to put a few tens of kilometers of cable on the map, and the hands of a colleague who has no direct relation to the IT specialty. Then the editor ymik rescued him for which he thanks a lot. It was with the finding of this post that the final choice was made in favor of Yandex.Map (instead of Google Maps).



After a while of work of a colleague, I received a text file of the following type (an example for one curve from 2 points, so as not to clutter up the place)



{ "name": "1", "center": { "lng": 37.62110402807594, "lat": 55.74879798509053, "zoom": 12 }, "styles": [{ "name": "ymikEditor#1315408925757#0", "style": { "lineStyle": { "strokeColor": "0000ff80", "strokeWidth": "5" } } }], "objects": [{ "style": "ymikEditor#1315408925757#0", "name": "1", "description": "1", "type": "Polyline", "points": [{ "lng": 37.62110402807594, "lat": 55.773576281653035 }, { "lng": 37.6235072873533, "lat": 55.74957254620929 }] }] } 




Stage 2





In the image and likeness of the work of ymik we will put these arts on the map.



Let's agree that our object is in the variable val, map -

Further a bit of code (it is simple, but the solution itself is simple, so let it be with comments)



Create a map and simple controls. As we see from the code, in the process JQuery is loaded, which we will use in the future (instead of $ using YMaps.jQuery).

 map = new YMaps.Map(YMaps.jQuery("#YMapsID")[0]); map.setCenter(new YMaps.GeoPoint(37.643347, 55.745478), 13); map.addControl(new YMaps.Zoom()); map.addControl(new YMaps.ToolBar()); map.addControl(new YMaps.ScaleLine()); map.addControl(new YMaps.TypeControl()); map.setType(YMaps.MapType.SATELLITE); //     var moscowBounds = new YMaps.GeoBounds( new YMaps.GeoPoint(37.389705, 55.577759), new YMaps.GeoPoint(37.844264, 55.91086) ); 




Line drawing function

 polylines=[]; function draw_lines() { k = 0; //  for (var j = 0; j < val['objects'].length; j++) //        { if (val['objects'][j]['type'] == 'Polyline') //         Points { Points = []; for (var i = 0; i < val['objects'][j]['points'].length; i++) { Points.push(new YMaps.GeoPoint(val['objects'][j]['points'][i]['lng'], val['objects'][j]['points'][i]['lat'])); } //        pl = new YMaps.Polyline(Points, { style: { lineStyle: { strokeColor: val['styles'][k]['style']['lineStyle']['strokeColor'], strokeWidth: val['styles'][k]['style']['lineStyle']['strokeWidth'] } }, hasHint: 1 }); pl.name = val['objects'][j]['name']; pl.description = val['objects'][j]['description']; polylines.push(pl); //    polylines map.addOverlay(pl); //    k++; } if (val['objects'][j]['type'] == 'Placemark') //      { var placemark = new YMaps.Placemark(new YMaps.GeoPoint(val['objects'][j]['points']['lng'], val['objects'][j]['points']['lat']), { style: val['objects'][j]['style'] }); placemark.name = val['objects'][j]['name']; placemark.description = val['objects'][j]['description']; map.addOverlay(placemark); } } } 




Stage 3



At this stage we will do the main work.



To begin with, it would be great to have a form right on the map where the user could fill in the address he needed and see how far the cable the workers had to go.



Here is an example of your own control from Yandex: http://api.yandex.ru/maps/jsapi/examples/mapcontrolscustomizing.html



According to this model, we will act.



I give the designer of this control

 function nearest_search(object_address) { //      this.onAddToMap = function (map, position) { this.container = YMaps.jQuery("<div class='YMaps-button'> <i class='YMaps-button-c YMaps-button-l'><i></i></i><i class='YMaps-button-m YMaps-cursor-pointer'><i></i> <form id='find_nearest_form' action='#' class='YMaps-button-caption'>  <input type='text' name='object_address' value='"+object_address+"' id='search_nearest_input' style='border:1px solid green;' size='22'/></form></i><i class='YMaps-button-c YMaps-button-r'><i></i></i></div>"); //          (   )      this.map = map; this.position = position || new YMaps.ControlPosition(YMaps.ControlPosition.TOP_LEFT, new YMaps.Size(0, 40)); //  ,    . // CSS-,     this.container.css({ position: "absolute", zIndex: YMaps.ZIndex.CONTROL, width: "280px", }); this.position.apply(this.container); this.container.appendTo(this.map.getContainer()); } //      this.onRemoveFromMap = function () { //      ,    . }; } 




A trigger on which, instead of the submit form, we will run the search function and calculate the distance.

 YMaps.jQuery('#find_nearest_form').submit(function () { showAddress(YMaps.jQuery("#search_nearest_input").val()); return false; }); 




Now let's make a version of the “Information” element from the original maps.yandex.ru

How to do it is perfectly told here http://api.yandex.ru/maps/articles/tasks/service.xml#how-to-use-create-information-tool



We don’t need geocoding in this place, so we’ll cut it down



 function InformationControl() { var geoResult, clickPlace, listener, map; //        this.onAddToMap = function (parentMap) { map = parentMap; map.addCursor(YMaps.Cursor.HELP); //      listener = YMaps.Events.observe(map, map.Events.Click, function (map, mEvent) { //    var clickPoint = mEvent.getGeoPoint(); //    (     ) if (geoResult) { map.removeOverlay(geoResult); result = null; } //    -   if (clickPlace) { map.removeOverlay(clickPlace); clickPlace = null; } //       clickPlace = new YMaps.Placemark(clickPoint); clickPlace.description = clickPoint.toString(); map.addOverlay(clickPlace); //     (  ) geotrack(clickPoint, clickPlace); }, this); } //        this.onRemoveFromMap = function () { map.removeCursor(YMaps.Cursor.HELP); //    ,     if (geoResult) {map.removeOverlay(geoResult); } //  if (punchline) {map.removeOverlay(punchline); } if (clickPlace) {map.removeOverlay(clickPlace); } //      listener.cleanup(); map = geoResult = clickPlace = listener = null; } } 




Stage 4



Hurray, we have all the buttons - let's start writing the main function of determining the distance.



 function geotrack(clickPoint, clickPlace) { //    -   if (punchline) { map.removeOverlay(punchline); } nearest = 100000000000; //   .     . nearest_point = new YMaps.GeoPoint(0, 0); //    for (var i = 0; i < polylines.length; i++) //          -  ,      { if (nearest > polylines[i].getClosestPoint(clickPoint)['point'].distance(clickPoint)) { nearest = polylines[i].getClosestPoint(clickPoint)['point'].distance(clickPoint); nearest_point = polylines[i].getClosestPoint(clickPoint)['point']; } } clickPlace.name = "   : " + YMaps.humanDistance(nearest); clickPlace.description = ""; punchline = new YMaps.Polyline([clickPoint, nearest_point], { style: { lineStyle: { strokeColor: "ffffffff", strokeWidth: 2 } }, hashint: 1 }); punchline.name = "      : " + YMaps.humanDistance(nearest); map.addOverlay(punchline); return clickPlace.name; } 




Stage 5



Let's search for coordinates at.

Again an example from Yandex http://api.yandex.ru/maps/jsapi/examples/geocoding.html



 function showAddress(value) { //     map.removeOverlay(geoResult); //   . strictBounds : true -       (moscowBounds) var geocoder = new YMaps.Geocoder(value, { results: 1, boundedBy: moscowBounds, strictBounds : true }); //       YMaps.Events.observe(geocoder, geocoder.Events.Load, function () { //    ,      //         if (this.length()) { geoResult = this.get(0); //,    -   map.addOverlay(geoResult); name = geotrack(geoResult['_point'], geoResult); //  geoResult['text'] = '<b>' + geoResult['text'] + '.<br> ' + name + '</b>'; map.panTo(geoResult['_point'], { flying: 1 }); //     } else { alert("  "); //TODO:   -   } }); //     YMaps.Events.observe(geocoder, geocoder.Events.Fault, function (geocoder, error) { alert(" : " + error); //TODO:   -   }) } 




Well, it remains for us to check whether something has come to GET (according to the concept of the site it should come from there) and if it does, use the request as the address we are looking for.



 var get = location.search; if(get != '') { object_address = decodeURI(get.split('?object_address=')[1]); if(object_address==" ") {object_address=""; } else {YMaps.jQuery('#find_nearest_form').submit(); } } else { object_address=''; } 





The entire source can be viewed from the links in the example.

I would be happy to comment.



* UPDATE * Finally figured out how to edit posts, so as I wanted to fix everything, I added everything.

Many thanks to all who helped.






Information on methods and objects:

api.yandex.ru/maps/jsapi/doc/ref/reference/map.xml

api.yandex.ru/maps/jsapi/doc/ref/reference/geopoint.xml

api.yandex.ru/maps/jsapi/doc/ref/reference/polyline.xml

Source: https://habr.com/ru/post/127999/



All Articles