Так уж выходит, что темы большинства моих статей так или иначе совпадают в тем, чем я в данный момент занимаюсь. Соответственно не так давно меня попросили написать скрипт расчета стоимости доставки, который должен был работать следующим образом:
Из этих двух условий понятно, что основная задача — определить, находится ли адрес внутри МКАД, и если нет, то рассчитать кратчайшее расстояние до него, и не просто прямой отрезок, а маршрут по дороге.
В этой статье вас ждут два готовых скрипта с формой, которые вы просто можете сохранить себе на рабочий стол в виде html-файла и они уже будут работать, отличие между ними в том, что второй скрипт просто определяет, находится ли адрес внутри МКАД и рассчитывает расстояние, а первый ещё и показыват маршрут на Яндекс Картах.
Сначала небольшое видео о том, как это будет работать.
Этот код не просто рассчитывает кратчайшее расстояние, но и чертит маршрут на карте. В коде я постарался оставить как можно больше комментариев, чтобы всё было просто и понятно.
И, как я уже говорил, вы можете просто скопировать его 1 в 1, сохранить на рабочий стол и всё у вас будет работать. А потом, когда разберетесь, что к чему, уже без проблем сможете интегрировать его в рассчет стоимости доставки интернет-магазина и т.п.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="pragma" content="no-cache"> <title>Определение расстояния до МКАД API 2.1</title> <!-- Подлючаем jQuery --> <script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script> <script> /* * задаём переменные, * в том числе массив координат каждого километра МКАД (mkad_points) */ var map = null, geocoder, router, i = 0, //mkad distance 0 - в мкаде, -1 - адрес определяется, -2 ошибка определения адреса, -3 - яндекс не работал mkad_points = [ [ 37.842663,55.774543 ], [ 37.842663,55.774543 ], [ 37.84269,55.765129 ], [ 37.84216,55.75589 ], [ 37.842232,55.747672 ], [ 37.841109,55.739098 ], [ 37.840112,55.730517 ], [ 37.839555,55.721782 ], [ 37.836968,55.712173 ], [ 37.832449,55.702566 ], [ 37.829557,55.694271 ], [ 37.831425,55.685214 ], [ 37.834695,55.676732 ], [ 37.837543,55.66763 ], [ 37.839295,55.658535 ], [ 37.834713,55.650881 ], [ 37.824948,55.643749 ], [ 37.813746,55.636433 ], [ 37.803083,55.629521 ], [ 37.793022,55.623666 ], [ 37.781614,55.617657 ], [ 37.769945,55.61114 ], [ 37.758428,55.604819 ], [ 37.747199,55.599077 ], [ 37.736949,55.594763 ], [ 37.721013,55.588135 ], [ 37.709416,55.58407 ], [ 37.695708,55.578971 ], [ 37.682709,55.574157 ], [ 37.668471,55.57209 ], [ 37.649948,55.572767 ], [ 37.63252,55.573749 ], [ 37.619243,55.574579 ], [ 37.600828,55.575648 ], [ 37.586814,55.577785 ], [ 37.571866,55.581383 ], [ 37.55761,55.584782 ], [ 37.534541,55.590027 ], [ 37.527732,55.59166 ], [ 37.512227,55.596173 ], [ 37.501959,55.602902 ], [ 37.493874,55.609685 ], [ 37.485682,55.616259 ], [ 37.477812,55.623066 ], [ 37.466709,55.63252 ], [ 37.459074,55.639568 ], [ 37.450135,55.646802 ], [ 37.441691,55.65434 ], [ 37.433292,55.66177 ], [ 37.425513,55.671509 ], [ 37.418497,55.680179 ], [ 37.414338,55.687995 ], [ 37.408076,55.695418 ], [ 37.397934,55.70247 ], [ 37.388978,55.709784 ], [ 37.38322,55.718354 ], [ 37.379681,55.725427 ], [ 37.37483,55.734978 ], [ 37.370131,55.745291 ], [ 37.369368,55.754978 ], [ 37.369062,55.763022 ], [ 37.369691,55.771408 ], [ 37.370086,55.782309 ], [ 37.372979,55.789537 ], [ 37.37862,55.796031 ], [ 37.387047,55.806252 ], [ 37.390523,55.81471 ], [ 37.393371,55.824147 ], [ 37.395176,55.832257 ], [ 37.394476,55.840831 ], [ 37.392949,55.850767 ], [ 37.397368,55.858756 ], [ 37.404564,55.866238 ], [ 37.417446,55.872996 ], [ 37.429672,55.876839 ], [ 37.443129,55.88101 ], [ 37.45955,55.882904 ], [ 37.474237,55.88513 ], [ 37.489634,55.889361 ], [ 37.503001,55.894737 ], [ 37.519072,55.901823 ], [ 37.529367,55.905654 ], [ 37.543749,55.907682 ], [ 37.559757,55.909418 ], [ 37.575423,55.910881 ], [ 37.590488,55.90913 ], [ 37.607035,55.904902 ], [ 37.621911,55.901152 ], [ 37.633014,55.898735 ], [ 37.652993,55.896458 ], [ 37.664905,55.895661 ], [ 37.681443,55.895106 ], [ 37.697513,55.894046 ], [ 37.711276,55.889997 ], [ 37.723681,55.883636 ], [ 37.736168,55.877359 ], [ 37.74437,55.872743 ], [ 37.75718,55.866137 ], [ 37.773646,55.8577 ], [ 37.780284,55.854234 ], [ 37.792322,55.848038 ], [ 37.807961,55.840007 ], [ 37.816127,55.835816 ], [ 37.829665,55.828718 ], [ 37.836914,55.821325 ], [ 37.83942,55.811538 ], [ 37.840166,55.802472 ], [ 37.841145,55.793925 ] ], minShir = mkad_points[0][0], minDolg = mkad_points[0][1], minDelta = 10000, minJ = 0, delta = 100000, ymapsAPIerror = true; // по умолчанию API яндекса не доступен /* * Функция вызывается при удачной загрузке API яндекса. */ function initMap() { map = new ymaps.Map(jQuery("#ymapsID")[0], { center: [55.745508, 37.435225], // куда ставить центр карты по умолчанию zoom: 13 // зум }); // Создание экземпляра карты и его привязка к созданному контейнеру ymapsAPIerror = false; // с API все в порядке } /* * Функция, проверяющая, находится ли указанная точка внутри МКАД * @param x широта * @param y долгота */ function inPoly( x, y ){ var j = mkad_points.length - 1, c = false; // true/false - внутри или вне полигона for (i = 0; i < mkad_points.length; i++){ if ((((mkad_points[i][1]<=y) && (y<mkad_points[j][1])) || ((mkad_points[j][1]<=y) && (y<mkad_points[i][1]))) && (x > (mkad_points[j][0] - mkad_points[i][0]) * (y - mkad_points[i][1]) / (mkad_points[j][1] - mkad_points[i][1]) + mkad_points[i][0])) { c = !c } j = i; } return c; } /* * Функция возващает полный адрес * тут уже в зависимости от вашей формы и от ваших конкретных задач, эта функция может быть другой */ function getFullAddress() { return ("Московская область, г. " + document.getElementById("city").value + " " + document.getElementById("moscow_address").value); } /* * Делаем некоторые замены в адресе, чтобы не было неточностей результата */ function refineAddress(address) { address = address.toUpperCase(); address = address.replace("ПРОСПЕКТ", ""); address = address.replace("ПРКТ", ""); address = address.replace("ПР-КТ", ""); address = address.replace("ПР-Т", ""); address = address.replace("ПРС-КТ", ""); address = address.replace("ПРС-Т", ""); address = address.replace("П-КТ", ""); address = address.replace("П-Т", ""); return address; } function getMKADDistance() { minShir = mkad_points[0][0]; minDolg = mkad_points[0][1]; minDelta = 10000; minJ = 0; delta = 100000; var geocoder = null; // если с API всё ок if(!ymapsAPIerror) { // начинаем геокодирование (определение координат по адресу) geocoder = new ymaps.geocode(refineAddress(getFullAddress())).then(function (res) { var firstGeoObject = res.geoObjects.get(0), // Координаты геообъекта. coords = firstGeoObject.geometry.getCoordinates(), Shir = coords[1], Dolg = coords[0]; // отправляем в консоль для дебага console.log("geocoder OK Shir=" + Shir +" Dolg=" + Dolg); // удаляем предыдущие маршруты с карты map.geoObjects.removeAll(); // добавляем балун map.balloon.open(coords, "Это здесь"); if (!inPoly(Shir,Dolg)) { // точка за мкадом, считаем маршрут console.log("Точка за пределами МКАД"); for (var j=0; j < mkad_points.length; j++) { // проверяем, какая из точек ближе всего delta = (mkad_points[j][0]-Shir)*(mkad_points[j][0]-Shir) + (mkad_points[j][1]-Dolg)*(mkad_points[j][1]-Dolg); console.log(delta + ' ' + minDelta); if(delta < minDelta) { minShir = mkad_points[j][0]; // широта ближайшей точки minDolg = mkad_points[j][1]; // долгота ближайшей точки minJ = j; minDelta = delta; } } router = new ymaps.route([ // Список точек, которые необходимо посетить - 1 точка "г. Москва МКАД " + minJ + " км", getFullAddress() ]).then(function (route) { var way = route.getPaths().get(0), segments = way.getSegments(), numOfSegments = segments.length, currSegment = numOfSegments-1, segment, Dist = 0; map.geoObjects.add(route); map.setBounds(way.geometry.getBounds()); while (currSegment >= 0) { segment = segments[currSegment]; // если данный сегмент маршрута строится по МКАД, не учитываем if (segment.getStreet().indexOf("МКАД")>=0) break; currSegment--; } for (i=currSegment+1; i < numOfSegments; i++) { Dist = Dist + segments[i].getLength(); } showResultKm(Dist); }, function (error) { console.log("Возникла ошибка: " + error.message); }); } else { //точка изначально внутри мкада, маршрут не считаем console.log("Точка внутри МКАД"); showResultKm(0); } }, function (error) { console.log("Возникла ошибка: " + error.message); }); } else { console.log("Проблема с API Яндекс Карт"); showResultKm(-3); } } /* * Просто выводит соответствующее сообщение */ function showResultKm(mkad_distance){ if( mkad_distance > 0 ) alert('Расстояние от МКАД до указанной точки:' + Math.ceil(mkad_distance / 1000) + ' км'); else alert('Адрес внутри МКАД'); } jQuery(function($){ // асинхронно грузим API яндекса $.getScript("https://api-maps.yandex.ru/2.1/?lang=ru_RU", function (response) { console.log(window); if(window.ymaps != undefined) { ymaps.load(initMap); } }); $('#calc').click(function(){ getMKADDistance(); }); }); </script> </head> <body> <p> Город <input class="userdata" type="text" name="city" id="city" value="Москва" size="20"> </p> <p> Адрес (улица, № дома, офиса, квартиры): <textarea class="userdata" name="address" id="moscow_address" style="width:100%; height: 60px;"></textarea> </p> <p> <button id="calc">Вывести расстояние и указать маршрут</button> </p> <!-- собственно сам контейнер карты, он вам не нужен, если вы не хотите отображать маршрут --> <div id="ymapsID" style="height:500px;"> </body></html>
Несколько комментариев по коду:
140-143
, 217
, 220
, 251-252
, 271-272
и 334
, либо просто перейдите к следующему готовому коду, в котором я уже всё закомментировал.8
, 306-315
— я привык работать с jQuery, поэтому здесь он используется для асинхронной загрузки API Яндекс Карт и для отслеживания клика по кнопке, но он не лежит в основе скрипта, поэтому вы легко сможете обойтись и без него. И опять-таки, на тот случай, если у вас с этим возникают трудности, следующий пример я сделал на чистом JavaScript.18-127
— массив координат (широта и долгота) каждого километра МКАД.Всё точно так же, код готовый к использованию — просто вставляете его и смотрите результат.
<!DOCTYPE html> <html> <head> <meta charset="utf-8"> <meta http-equiv="pragma" content="no-cache"> <title>Определение расстояния до МКАД API 2.1</title> <!-- Подлючаем API карт --> <script src="https://api-maps.yandex.ru/2.1/?lang=ru_RU"></script> <script> /* * задаём переменные, * в том числе массив координат каждого километра МКАД (mkad_points) */ var map = null, geocoder, router, i = 0, //mkad distance 0 - в мкаде, -1 - адрес определяется, -2 ошибка определения адреса, -3 - яндекс не работал mkad_points = [ [ 37.842663,55.774543 ], [ 37.842663,55.774543 ], [ 37.84269,55.765129 ], [ 37.84216,55.75589 ], [ 37.842232,55.747672 ], [ 37.841109,55.739098 ], [ 37.840112,55.730517 ], [ 37.839555,55.721782 ], [ 37.836968,55.712173 ], [ 37.832449,55.702566 ], [ 37.829557,55.694271 ], [ 37.831425,55.685214 ], [ 37.834695,55.676732 ], [ 37.837543,55.66763 ], [ 37.839295,55.658535 ], [ 37.834713,55.650881 ], [ 37.824948,55.643749 ], [ 37.813746,55.636433 ], [ 37.803083,55.629521 ], [ 37.793022,55.623666 ], [ 37.781614,55.617657 ], [ 37.769945,55.61114 ], [ 37.758428,55.604819 ], [ 37.747199,55.599077 ], [ 37.736949,55.594763 ], [ 37.721013,55.588135 ], [ 37.709416,55.58407 ], [ 37.695708,55.578971 ], [ 37.682709,55.574157 ], [ 37.668471,55.57209 ], [ 37.649948,55.572767 ], [ 37.63252,55.573749 ], [ 37.619243,55.574579 ], [ 37.600828,55.575648 ], [ 37.586814,55.577785 ], [ 37.571866,55.581383 ], [ 37.55761,55.584782 ], [ 37.534541,55.590027 ], [ 37.527732,55.59166 ], [ 37.512227,55.596173 ], [ 37.501959,55.602902 ], [ 37.493874,55.609685 ], [ 37.485682,55.616259 ], [ 37.477812,55.623066 ], [ 37.466709,55.63252 ], [ 37.459074,55.639568 ], [ 37.450135,55.646802 ], [ 37.441691,55.65434 ], [ 37.433292,55.66177 ], [ 37.425513,55.671509 ], [ 37.418497,55.680179 ], [ 37.414338,55.687995 ], [ 37.408076,55.695418 ], [ 37.397934,55.70247 ], [ 37.388978,55.709784 ], [ 37.38322,55.718354 ], [ 37.379681,55.725427 ], [ 37.37483,55.734978 ], [ 37.370131,55.745291 ], [ 37.369368,55.754978 ], [ 37.369062,55.763022 ], [ 37.369691,55.771408 ], [ 37.370086,55.782309 ], [ 37.372979,55.789537 ], [ 37.37862,55.796031 ], [ 37.387047,55.806252 ], [ 37.390523,55.81471 ], [ 37.393371,55.824147 ], [ 37.395176,55.832257 ], [ 37.394476,55.840831 ], [ 37.392949,55.850767 ], [ 37.397368,55.858756 ], [ 37.404564,55.866238 ], [ 37.417446,55.872996 ], [ 37.429672,55.876839 ], [ 37.443129,55.88101 ], [ 37.45955,55.882904 ], [ 37.474237,55.88513 ], [ 37.489634,55.889361 ], [ 37.503001,55.894737 ], [ 37.519072,55.901823 ], [ 37.529367,55.905654 ], [ 37.543749,55.907682 ], [ 37.559757,55.909418 ], [ 37.575423,55.910881 ], [ 37.590488,55.90913 ], [ 37.607035,55.904902 ], [ 37.621911,55.901152 ], [ 37.633014,55.898735 ], [ 37.652993,55.896458 ], [ 37.664905,55.895661 ], [ 37.681443,55.895106 ], [ 37.697513,55.894046 ], [ 37.711276,55.889997 ], [ 37.723681,55.883636 ], [ 37.736168,55.877359 ], [ 37.74437,55.872743 ], [ 37.75718,55.866137 ], [ 37.773646,55.8577 ], [ 37.780284,55.854234 ], [ 37.792322,55.848038 ], [ 37.807961,55.840007 ], [ 37.816127,55.835816 ], [ 37.829665,55.828718 ], [ 37.836914,55.821325 ], [ 37.83942,55.811538 ], [ 37.840166,55.802472 ], [ 37.841145,55.793925 ] ], minShir = mkad_points[0][0], minDolg = mkad_points[0][1], minDelta = 10000, minJ = 0, delta = 100000, ymapsAPIerror = false; // с API все в порядке /* * Функция, проверяющая, находится ли указанная точка внутри МКАД * @param x широта * @param y долгота */ function inPoly( x, y ){ var j = mkad_points.length - 1, c = false; // true/false - внутри или вне полигона for (i = 0; i < mkad_points.length; i++){ if ((((mkad_points[i][1]<=y) && (y<mkad_points[j][1])) || ((mkad_points[j][1]<=y) && (y<mkad_points[i][1]))) && (x > (mkad_points[j][0] - mkad_points[i][0]) * (y - mkad_points[i][1]) / (mkad_points[j][1] - mkad_points[i][1]) + mkad_points[i][0])) { c = !c } j = i; } return c; } /* * Функция возващает полный адрес * тут уже в зависимости от вашей формы и от ваших конкретных задач, эта функция может быть другой */ function getFullAddress() { return ("Московская область, г. " + document.getElementById("city").value + " " + document.getElementById("moscow_address").value); } /* * Делаем некоторые замены в адресе, чтобы не было неточностей результата */ function refineAddress(address) { address = address.toUpperCase(); address = address.replace("ПРОСПЕКТ", ""); address = address.replace("ПРКТ", ""); address = address.replace("ПР-КТ", ""); address = address.replace("ПР-Т", ""); address = address.replace("ПРС-КТ", ""); address = address.replace("ПРС-Т", ""); address = address.replace("П-КТ", ""); address = address.replace("П-Т", ""); return address; } function getMKADDistance() { minShir = mkad_points[0][0]; minDolg = mkad_points[0][1]; minDelta = 10000; minJ = 0; delta = 100000; var geocoder = null; // если с API всё ок if(!ymapsAPIerror) { // начинаем геокодирование (определение координат по адресу) geocoder = new ymaps.geocode(refineAddress(getFullAddress())).then(function (res) { var firstGeoObject = res.geoObjects.get(0), // Координаты геообъекта. coords = firstGeoObject.geometry.getCoordinates(), Shir = coords[1], Dolg = coords[0]; // отправляем в консоль для дебага console.log("geocoder OK Shir=" + Shir +" Dolg=" + Dolg); if (!inPoly(Shir,Dolg)) { // точка за мкадом, считаем маршрут console.log("Точка за пределами МКАД"); for (var j=0; j < mkad_points.length; j++) { // проверяем, какая из точек ближе всего delta = (mkad_points[j][0]-Shir)*(mkad_points[j][0]-Shir) + (mkad_points[j][1]-Dolg)*(mkad_points[j][1]-Dolg); console.log(delta + ' ' + minDelta); if(delta < minDelta) { minShir = mkad_points[j][0]; // широта ближайшей точки minDolg = mkad_points[j][1]; // долгота ближайшей точки minJ = j; minDelta = delta; } } router = new ymaps.route([ // Список точек, которые необходимо посетить - 1 точка "г. Москва МКАД " + minJ + " км", getFullAddress() ]).then(function (route) { var way = route.getPaths().get(0), segments = way.getSegments(), numOfSegments = segments.length, currSegment = numOfSegments-1, segment, Dist = 0; while (currSegment >= 0) { segment = segments[currSegment]; // если данный сегмент маршрута строится по МКАД, не учитываем if (segment.getStreet().indexOf("МКАД")>=0) break; currSegment--; } for (i=currSegment+1; i < numOfSegments; i++) { Dist = Dist + segments[i].getLength(); } showResultKm(Dist); }, function (error) { console.log("Возникла ошибка: " + error.message); }); } else { //точка изначально внутри мкада, маршрут не считаем console.log("Точка внутри МКАД"); showResultKm(0); } }, function (error) { console.log("Возникла ошибка: " + error.message); }); } else { console.log("Проблема с API Яндекс Карт"); showResultKm(-3); } } /* * Просто выводит соответствующее сообщение */ function showResultKm(mkad_distance){ if( mkad_distance > 0 ) alert('Расстояние от МКАД до указанной точки:' + Math.ceil(mkad_distance / 1000) + ' км'); else alert('Адрес внутри МКАД'); } </script> </head> <body> <p> Город <input class="userdata" type="text" name="city" id="city" value="Москва" size="20"> </p> <p> Адрес (улица, № дома, офиса, квартиры): <textarea class="userdata" name="address" id="moscow_address" style="width:100%; height: 60px;"></textarea> </p> <p> <button id="calc" onclick="getMKADDistance();">Вывести расстояние и указать маршрут</button> </p> </body></html>
Здравствуйте, Михаил
Не могли бы вы доработать код, чтобы адрес "подсказывался" во время набора?
И еще было бы здорово чтобы после ввода адреса карта переходила на этот адрес и приближалась к нему.
Здравствуйте,
не уверен, что я сейчас смогу найти время,
но относительно подсказывания адреса могу сделать наводку: https://tech.yandex.ru/maps/doc/jsapi/2.1/ref/reference/SuggestView-docpage/
А почему у вас комментарий без имени и емайл?
Видимо я не вошел под логином.
Если можете - добавьте пожалуйста данный функционал. Почти не разбираюсь в js.
Это было просто. Нашел код в интернете. Добавьте и себе
Спасибо!
Существует ли способ отучить woocommerce показывать сообщение "Введите свой адрес для просмотра параметров доставки", а сразу показывать все возможные методы доставки на странице оформления заказа для незарегистрированных пользователей?
Просто на сайте регистрация не предусмотрена. Два метода доставки имеют нулевую стоимость, а один ограничен диапазонами индексов и цена там одна. Стандартное местоположение клиента - Адрес магазина. Правил файл cart/cart-shipping.php, безрезультатно.
Техподдержка Woocommerce дала вариант, все методы доставки убрать - За пределы Вашей зоны доставки и тогда будет ОК. Ну да будет, но как тогда с диапазонами индексов работать )))))))
Скорее всего способ существует!
Добрый вечер, Миша
Самое интересное, что год назад, при равных настройках все работало прекрасно! Но после обновление woocommerce и переходе с php7.0 на php7.1 и выше, появились эти приколы ((( Откат версий проблему не решит.
Несколько человек писали в техподдержку, но вразумительного ответа в течении года так и не получили. По факту, в настройках магазина, Адрес магазина - стандартное местоположение клиента и доставка - параметры доставки - Спрятать стоимость доставки, пока адрес не введен, своих функций не выполняют.
Вот и приходится долгими ночами........))))))
Добрый вечер!
Сочувствую, желаю, чтобы у вас получилось разобраться! 💪