Материал, который мне понадобился для создания этого приложения, представлял собой просто файл .csv, содержащий список крупных магазинов (позже я узнал, что это Target). И задача, которая была поставлена ​​передо мной вместе с этим списком, заключалась в следующем: найти ближайший магазин по любому заданному адресу, не используя Google Distance API.

Вот ссылка на веб-страницу, на которой размещен этот проект, если вы хотите сначала увидеть, что он делает. Нажмите на: Магазин рядом со мной

Если вы просто хотите сразу перейти к коду, не читая моих объяснений, вот ссылка на мой репозиторий. Однако вы можете захотеть узнать, как это сделать, а также обо всех камнях преткновения, поэтому я объяснил это на примерах кода.

Вот ссылка на исходный файл .csv, если вы хотите скачать его и работать вместе со мной.

[Примечание: «$» во всем приведенном ниже коде представляет собой bash в терминале Mac. Код, который нужно ввести, находится рядом с ним, сам «$» вводить не нужно.]

Я начал с создания каталога для моего проекта в корневом каталоге:

$ cd
$ mkdir StoreNearMe
$ cd StoreNearMe

Затем я создал базовый html-файл, содержащий имя приложения и jQuery cdn.

$ touch find_store.html

Я использую Visual Studio Code в качестве текстового редактора (IDE). Итак, я перехожу к нему из командной строки:

$ code .

Я зашел в свой файл find_store.html в Visual Studio Code и добавил приведенный ниже код.

[Чтобы получить только базовый код HTML-файла в Visual Studio Code, используйте ярлык !. Просто откройте html-файл и введите !, вы получите весь код от ‹!DOCTYPE ….. до ‹/head›. Все, что вам нужно сделать, это добавить название вашего приложения между тегами ‹title› ‹/title›. В случае с этим приложением я также добавил jQuery cdn между тегами ‹script› ‹/script›.]

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8">
<meta name=”viewport” content=”width=device-width, initial-scale=1.0">
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
<title>StoreNearMe</title>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
</head>

Затем я получил файл csv и преобразовал его в json (этот шаг может быть ненужным, но мне было проще заставить jQuery читать его после преобразования) следующим образом:

//var csv is the CSV file with headers
function csvJSON(csv){

  var lines=csv.split("\n");

  var result = [];

  var headers=lines[0].split(",");

  for(var i=1;i<lines.length;i++){

      var obj = {};
      var currentline=lines[i].split(",");

      for(var j=0;j<headers.length;j++){
          obj[headers[j]] = currentline[j];
      }

      result.push(obj);

  }

  //return result; //JavaScript object
  return JSON.stringify(result); //JSON
}

[Я взял приведенный выше код из этого ответа StackOverflow: нажмите, чтобы увидеть код]

Затем я создал файл findStore.js и начал писать код jQuery для доступа к файлу json (который я назвал parsed.json) и читать в нем адреса.

//get the locations of the stores listed in the parsed.json file
function getStoreLocs() {
    let url = ‘parsed.json’;
    $.getJSON(url, function (json) {
        let storeArr = json;
    })
}
getStoreLocs()

Затем я создал поле ввода и кнопку «Перейти» на странице find_store.html для адреса, по которому приложение должно найти ближайший к нему магазин. Я также добавил теги script внизу, чтобы получить доступ к нашему файлу findStore.js из этого html-файла.

<body>
<header>
        <h1>StoreNearMe</h1>
</header>
<div>
      <input type=”text” id=”addr” placeholder=”Enter your address”>
      <button id=”addbtn”>Go</button>
</div>
</body>
<script src=”findStore.js”></script>
</html>

Вернемся к файлу findStore.js, чтобы создать функцию для получения этого адреса и его анализа:

//get the address from the input box and pass it to the function that searches for its latitude and longitude
function getAdd() {
    $(“#addbtn”).on(‘click’, function () {
        myFetch($(“#addr”).val())
    })
}
getAdd()

Функцию myFetch, которую я вызвал выше, еще нужно написать. Он будет принимать информацию, указанную в строке поиска (адрес, к которому вы хотите найти ближайший магазин), в качестве параметра, использовать внешний API геокарты и находить его широту и долготу. Затем он просмотрит файл parsed.json и найдет ближайший магазин на основе этих географических координат.

Вот функция myFetch в файле findStore.js:

//take the address from the input box, search the nominatim open street map search engine for the address’ geographical coordinates and set them to variables
function myFetch(address) {
    const proxyUrl = ‘https://cors-anywhere.herokuapp.com/';
    const queryUrl =      encodeURI(‘https://nominatim.openstreetmap.org/search/?limit=1&format=json&q=' + address)
    fetch(proxyUrl + queryUrl)
        .then(blob => blob.json())
        .then(data => {
          let addLat = data[0].lat
          addLat = parseFloat(addLat)
          let addLong = data[0].lon
          addLong = parseFloat(addLong)
          getStoreLocs(addLat, addLong)
})
}

Приведенная выше функция сначала принимает адрес в качестве параметра. Поскольку myFetch вызывается в функции getAdd, указанной выше, адрес является значением ввода (адрес, который был введен для поиска).

Затем функция устанавливает proxyUrl для решения любых проблем с точки зрения CORS. (нажмите CORS, чтобы узнать, что это такое.) Затем он создает переменную const queryUrl и устанавливает ее для внешнего API геокартирования с открытым исходным кодом и получает доступ к своей открытой карте улиц. Он использует адрес, который был передан этой функции в качестве параметра в качестве поискового запроса.

Затем функция использует встроенный в JavaScript promise API (щелкните для объяснения), fetch (щелкните для получения подробной информации об API), чтобы получить данные из proxyUrl и queryUrl, и с помощью .then() перемещается по данные, чтобы получить широту и логику адреса и установить их в переменные, которые можно использовать. Затем эти переменные передаются функции getStoreLocs, которую мы уже написали, вызывая эту функцию здесь и отправляя ей эти переменные в качестве параметров.

Теперь нам нужно вернуться и исправить эту функцию getStoreLocs следующим образом (добавить переменные параметров в скобках), чтобы получить параметры из функции myFetch:

//get the locations of the stores listed in the parsed.json file
function getStoreLocs(addLat, addLong) {
     let url = ‘parsed.json’;
       $.getJSON(url, function (json) {
        let storeArr = json;
     })
}
getStoreLocs()

Прежде чем мы перейдем к функции, которая будет принимать широту и логику входного адреса и находить ближайший к нему магазин, нам нужно решить, как рассчитать географическое расстояние между двумя точками, используя широту и долготу. (Это потому, что мы не используем для этого Distance API Google.)

Я искал вокруг, чтобы найти лучший способ определения географического расстояния, и нашел несколько способов, включая манхэттенское расстояние и евклидово расстояние. Я также нашел это обсуждение вычислений на StackOverflow: нажмите здесь. Также много такого вида кода: нажмите, чтобы посмотреть.

Наконец, я решил использовать этот код, который использует формулу Хаверсина для расчета расстояния:

//function to calculate distance using the Haversine formula that uses the radius of the earth to measure the distance
function getDistance(lat1, lon1, lat2, lon2, unit = “M”) {
        if ((lat1 === lat2) && (lon1 === lon2)) {
            return 0;
        }
        else {
           let radlat1 = Math.PI * lat1 / 180;
           let radlat2 = Math.PI * lat2 / 180;
           let theta = lon1 — lon2;
           let radtheta = Math.PI * theta / 180;
           let dist = Math.sin(radlat1) * Math.sin(radlat2) + Math.cos(radlat1) * Math.cos(radlat2) * Math.cos(radtheta);
       if (dist > 1) {
              dist = 1;
          }
        dist = Math.acos(dist);
        dist = dist * 180 / Math.PI;
        dist = dist * 60 * 1.1515;
       if (unit === “K”) { dist = dist * 1.609344 }
       if (unit === “N”) { dist = dist * 0.8684 }
return dist;
}
}

Я взял код этой самой математической функции из этого поста: нажмите, чтобы увидеть пост, который также очень хорошо объясняет приведенный выше код. Затем я написал эту функцию, которая берет все данные из функций выше и вычисляет магазин, ближайший к указанному адресу.

//use the address geographical coordinates and the coordinates found in the parsed json file to calculate which is the store nearest to that address. Set the map to the address location and the marker to the store location
function findNearest(addLat, addLong, storeArr) {
    let distance = null;
    let nearest = null;
    let latLong = [];
    storeArr.forEach((location) => {
    let storeLat = (location[‘Latitude’])
     storeLat = parseFloat(storeLat)
     let storeLong = (location[‘Longitude’])
     storeLong = parseFloat(storeLong)
     let dist = getDistance(addLat, addLong, storeLat, storeLong)
     dist = parseFloat(dist);
     if (distance === null || dist < distance) {
     distance = dist;
     distance = parseFloat(distance)
     nearest = location;
     latLong = [storeLat, storeLong] 
    }
    });
return {     addLatLong: addLatLong,
    location: nearest, distance: distance, latLong: latLong }
}

Прежде чем мы проанализируем вышеприведенную функцию, нам нужно вернуться к нашему старому getStoreLocs и передать широту и логику входного адреса, а также массив, содержащий местоположения магазина, в функцию findNearest, вызвав ее там и передав их в качестве параметров:

//get the locations of the stores listed in the parsed.json file
function getStoreLocs(addLat, addLong) {
   let url = ‘parsed.json’;
    $.getJSON(url, function (json) {
    let storeArr = json;
    findNearest(addLat, addLong, storeArr)
   })
}
getStoreLocs()

После того, как мы передали широту и долготу адреса, а также массив json, содержащий адреса всех магазинов, в функцию findNearest выше, все, что нам нужно сделать, это отобрать широту и долготу магазинов в переменные, вызвать функцию getDistance, которую мы написали. , передайте широту и долготу адреса, а также широту и логиту магазинов, чтобы вычислить ближайшее из них по формуле Хаверсина. Функция возвращает ближайший магазин.

Таким образом, серверная часть jQuery построена, но у нас все еще нет этих деталей, чтобы показать их на нашей html-странице. Мы могли бы, конечно, просто вернуть все эти данные на html-страницу в виде текстового поля, но я подумал, что у нас также может быть карта, показывающая, где находится ближайший магазин. И решил использовать для этого Leaflet.js.

Опять же, мы начинаем с добавления cdn Leaftlet.js в наш файл find_store.html:

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8">
<meta name=”viewport” content=”width=device-width, initial-scale=1.0">
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
    <title>StoreNearMe</title>
   <link rel=”stylesheet” href=”https://unpkg.com/[email protected]/dist/leaflet.css" />
     <script src=”https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
</head>

(Примечание. Порядок, в котором даны ссылки на различные файлы, важен, так как JavaScript выполняет их один за другим.)

Затем мы добавляем div в find_store.html для хранения карты и придания ей необходимых элементов стиля, как это предлагается в примерах на веб-сайте Leaflet.js:

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8">
<meta name=”viewport” content=”width=device-width, initial-scale=1.0">
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
       <title>StoreNearMe</title>
<link rel=”stylesheet” href=”https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src=”https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<style>
#mapid {
    height: 800px;
     width: 1200px;
    border: 1px solid black;
}
</style>
</head>
<body>
<header>
     <h1>StoreNearMe</h1>
</header>
<div id=wrapper>
<div>
    <input type="text" id="addr" placeholder="Enter your address"> .
     <button id="addbtn">Go</button>
       <textarea id="storeadd" type="text"  placeholder="Distance to store" ></textarea>
</div>
<div id="mapid"></div>
</div>
</body>
<script src="js/findStore.js"></script>
</html>

Как вы заметили, я также добавил текстовую область в html, чтобы цифры отображали расстояние между двумя точками. Теперь добавим код Leaflet.js в файл findStore.js.

Добавьте myMap, mylatLng и myMarker в качестве глобальных переменных вверху файла:

let mymap, mylatLng, marker;

Затем измените код в функции findNearest() следующим образом:

//use the address geographical coordinates and the coordinates found in the parsed json file to calculate which is the store nearest to that address. Set the map to the address location and the marker to the store location
function findNearest(addLat, addLong, storeArr) {
    let distance = null;
    let nearest = null;
    let latLong = [];
    storeArr.forEach((location) => {
      let storeLat = (location[‘Latitude’])
      storeLat = parseFloat(storeLat)
      let storeLong = (location[‘Longitude’])
      storeLong = parseFloat(storeLong)
      let dist = getDistance(addLat, addLong, storeLat, storeLong)
      dist = parseFloat(dist);
      if (distance === null || dist < distance) {
      distance = dist;
      distance = parseFloat(distance)
      nearest = location;
      latLong = [storeLat, storeLong]
     }
    })
     let addLatLong = [addLat, addLong]
     initialLatLong = addLatLong
     mylatLng = latLong
     marker.setLatLng(mylatLng).update();
     let storeadd = ‘’;
     $.each(nearest, function (key, value) {
      storeadd += ‘ ‘ + key + ‘ ‘ + value;
    });
     marker.setPopupContent(storeadd);
     $(“#storeadd”).val(“Distance: “ + distance + “ miles”)
    view = mymap.setView(mylatLng, 15).update();
}

В приведенной выше функции мы обновили mylatLng, используя marker.setLatLng() из Leaflet.Js. Мы также установили маркер, чтобы показать адрес магазина, и мы дали текстовой области в файле html детали расстояния, чтобы показать отчетливо и отдельно. Затем мы изменили представление для обновления с указанием местоположения ближайшего магазина на mymap.setView(mylatLng, 15) [15 здесь — степень масштабирования, а mylatLng — это переменная, которую мы установили для отображения широты и долготы точки на карте. карта).

Это еще не конец. У нас должна быть функция для создания самой карты с помощью Leaflet.js! Вот он, в конце файла findStore.js:

function initializeMap() {
//initialize map with sample latitude and longitude
   let initialLatLng = [35.9957436, -78.9012117]
    //this map has to go into our mapid div in the find_store.html file
   mymap = L.map(‘mapid’).setView(initialLatLng, 15);
  //get the initial map from the Leaflet API with the tile details
  const osm = L.tileLayer(‘https://{s}.tile.osm.org/{z}/{x}/{y}.png', {
attribution: ‘&copy; <a href=”https://osm.org/copyright">OpenStreetMap</a> contributors’
}).addTo(mymap);
//set initial marker
   mylatLng = [35.966045, -78.9587215]
   marker = L.marker(mylatLng).addTo(mymap);
   marker.setLatLng(mylatLng).update();
}
//initialize this map as soon as the page is opened
$(document).ready(initializeMap);

Хорошо, мы почти закончили с нашим бэкэндом jQuery и интерфейсным приложением Leaftlet.js. Я только что добавил простой CSS для нашей html-страницы, который вы, возможно, захотите использовать.

Откройте файл find_store.css и вставьте в него этот код: (вы можете изменить этот код, чтобы он соответствовал любым цветам и формам, которые вы хотите использовать.)

input {
height: 60px;
border: 1px black solid;
border-radius: 15%;
font: 16px;
text-align: center;
}
textarea {
border: 1px black solid;
margin-left: 20px;
}
header {
background-color: cornsilk;
height: 60px;
margin-bottom: 1%;
}

Не забудьте связать свой файл CSS с файлом find_store.html. Добавьте это в голову следующим образом:

<!DOCTYPE html>
<html lang=”en”>
<head>
<meta charset=”UTF-8">
<meta name=”viewport” content=”width=device-width, initial-scale=1.0">
<meta http-equiv=”X-UA-Compatible” content=”ie=edge”>
    <title>StoreNearMe</title>
<link rel=”stylesheet” href=”find_store.css”>
<link rel=”stylesheet” href=”https://unpkg.com/[email protected]/dist/leaflet.css" />
<script src=”https://unpkg.com/[email protected]/dist/leaflet.js"></script>
<script src=”https://ajax.googleapis.com/ajax/libs/jquery/2.2.0/jquery.min.js"></script>
<style>
#mapid {
height: 800px;
width: 1200px;
border: 1px solid black;
}
</style>
</head>

Вот и все. У нас есть работающее приложение. Если вам понравился этот урок, хлопайте в ладоши, делитесь им и используйте по своему усмотрению!

Если вы хотите связаться со мной, просто нажмите:

ЛинкедИн

"Электронное письмо"

Гитхаб

"Веб-сайт"

Все предложения, критика приветствуются! С нетерпением жду ответа от вас всех.