Несколько лет назад мой приятель ( Райан ЛеТулле ) продемонстрировал классное приложение на карте недвижимости, которое он создал. Я попросил его написать в блоге (потому что у всех есть блог и время для блога, верно?), Но он так и не нашел его. На прошлой неделе я спросил его, не возражает ли он, чтобы я восстановил то, что я видел, как он делает, и он сказал, что сделай это. Спасибо, Райан, за вдохновение!
Вы когда-нибудь пытались использовать сайт по недвижимости, чтобы найти дома, но не могли сузить критерии поиска? Например, вы знаете, в каком городе вы хотите жить, но также и в каком районе. Если вам повезет, сайт позволит вам искать по подразделениям, но как, черт возьми, вы это выясните? Мое подразделение — Айвенго, но я жил здесь около 10 лет, прежде чем понял это.
Я думаю, что вам действительно хотелось бы получить критерии поиска. Учитывая карту города и смутное представление о том, где вы хотите жить, что, если бы мы могли нарисовать регион и найти в нем результаты?
Эта идея опирается на два основных аспекта. Во-первых, это возможность рисовать на карте. Я собираюсь использовать Google Maps для этого проекта. Карты Google — чертовски глубокий API. Это позволяет рисовать маркеры, линии, прямоугольники и многоугольники в целом на карте. Это также позволяет глубоко взаимодействовать с этими элементами пользовательского интерфейса.
Исследуя эту идею, я обнаружил, что у них есть функция под названием «Редактируемые пользователем формы» . Как вы можете себе представить, это позволяет пользователю перемещать и корректировать фигуру на карте:
Это хорошо — но коробка не совсем точна. Я решил начать с простых строк (то, что Google называет Polylines). Я создал карту, а затем использовал события щелчка, чтобы добавить маркеры с линиями, соединяющими их. К счастью, у Google уже был пример этого ( комплекс Polyline ), поэтому я начал с этого. Я сделал одну модификацию, хотя. Я настроил его так, чтобы, как только вы нажали три раза, я автоматически «закрыл» окно для вас.
Например:
Код довольно прост — просто обратите внимание, сколько линий мы нарисовали, и при третьем щелчке автоматически закройте окно:
function initialize() { var mapOptions = { center: new google.maps.LatLng(-34.397, 150.644), zoom: 8, mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById("map-canvas"),mapOptions); var polyOptions = { strokeColor: '#000000', strokeOpacity: 1.0, strokeWeight: 3 } var markers = []; poly = new google.maps.Polyline(polyOptions); poly.setMap(map); google.maps.event.addListener(map, 'click', function(event) { var path = poly.getPath(); path.push(event.latLng); markers.push(new google.maps.Marker({ position: event.latLng, title: '#' + path.getLength(), map: map })); if(path.getLength() == 4) { console.log('box it'); path.push(markers[0].getPosition()); console.dir(markers); } }); } google.maps.event.addDomListener(window, 'load', initialize);
Это сработало … Хорошо (и вы можете продемонстрировать это здесь ), но мне стало плохо, когда я заставлял вас искать внутри четырехстороннего многоугольника. Что я действительно хотел, так это возможность позволять вам нажимать столько раз, сколько вы хотите, и когда вы закончите, автоматически закрывайте «ящик». В качестве примера я создал многосегментный многоугольник здесь и щелкнул поиск, чтобы завершить регион:
В целом, я чувствовал, что это было хорошее решение. Теперь, прямо сейчас, вы можете спросить, что произойдет, если вы нарисуете что-то сумасшедшее, как, ну, скажем так:
Не делай этого. Шутки в сторону.
Хорошо … Итак, на данный момент мы рассмотрели первый основной аспект этого проекта — дать вам возможность «нарисовать» регион. Теперь приходит второй. Учитывая, что мы знаем регион, как мы можем найти в нем дерьмо?
Я был немного разорван об этом аспекте. Оказывается, есть решение на стороне клиента, предоставленное Google (не удивительно), но также и код на стороне сервера, который вы также можете использовать. У CFLib есть UDF ( PointInPolygon ), которому почти 10 лет, и он будет этим заниматься. Таким образом, теоретически мы можем получить доступ к базе данных сервера, выполнить некоторую логику и вернуть очки. Или мы могли бы сделать это на стороне клиента. Хранение данных на сервере обеспечивает более быструю начальную загрузку. Хранение этого в клиенте позволяет нам искать немного быстрее, но делает первую загрузку «толще».
Я решил использовать клиентское решение главным образом потому, что хотел протестировать этот конкретный API Google, а также потому, что мне было любопытно, насколько «плохим» будет удар, если я сохраню большой набор данных на клиенте. В моем примере я храню только данные долготы / широты. Я полагаю, что, как только вы нажмете на результат, мы можем выполнить пинг AJAX, чтобы получить дополнительные данные. Или, черт возьми, мы можем сделать это, как только вы закончите свой полигон. Я написал скрипт (вы можете посмотреть исходный код здесь ), чтобы сгенерировать 400 различных длинных / латовых пар, которые были примерно в районе Лафайетта. Этот скрипт выводит переменную JavaScript, которую я мог бы затем сохранить и сохранить в файле с именем data.json.
Теперь, когда у меня были данные, я мог обновить свой код, чтобы загрузить его в память. Что касается того, как я мог фильтровать данные, я использовал библиотеку Google Geometry . Он позволяет вам передать точку long / lat и многоугольник, и он возвращает true, если точка находится внутри него. Вот обновленное издание. Вы хотите обратить внимание на функцию doSearch:
function initialize() { var locData; var searchButton = document.querySelector("#searchButton"); var resultsDiv = document.querySelector("#results"); var mapOptions = { center: new google.maps.LatLng(30.223178, -92.024231), zoom: 12, mapTypeId: google.maps.MapTypeId.ROADMAP }; var map = new google.maps.Map(document.getElementById("map-canvas"),mapOptions); var polyOptions = { strokeColor: '#000000', strokeOpacity: 1.0, strokeWeight: 3 } var blueIcon = { url:"http://www.google.com/intl/en_us/mapfiles/ms/micons/blue-dot.png" }; var markers = []; poly = new google.maps.Polyline(polyOptions); poly.setMap(map); google.maps.event.addListener(map, 'click', function(event) { var path = poly.getPath(); path.push(event.latLng); markers.push(new google.maps.Marker({ position: event.latLng, title: '#' + path.getLength(), icon:blueIcon, map: map })); }); searchButton.addEventListener("click", function(e) { var path = poly.getPath(); if(path.getLength() < 4) { alert('Please select at least 4 points.'); return; } path.push(markers[0].getPosition()); searchButton.setAttribute("disabled","disabled"); doSearch(); }); $.get("data.json", {}, function(res) { locData=res; searchButton.innerText = "Search"; searchButton.removeAttribute("disabled");; }); function doSearch() { results.innerHTML = "<i>Searching for matches...</i>"; var totalFound = 0; for(var i=0; i<locData.length; i++) { //I should probably cache this creation! var point = new google.maps.LatLng(locData[i][0],locData[i][1]); if(google.maps.geometry.poly.containsLocation(point,poly)) { var m = new google.maps.Marker({ position: point, title: 'Location '+i, map: map }); totalFound++; } } results.innerHTML = "Found " + totalFound + " results."; } } google.maps.event.addDomListener(window, 'load', initialize);
Как видите, я перебираю данные и передаю их в Geometry API. Если совпадение найдено, я добавляю маркер. Когда все будет готово, я сообщу об итогах матчей. Вы можете продемонстрировать это, нажав гигантскую демонстрационную кнопку ниже. Обратите внимание, что двойное нажатие кнопки «Поиск» уничтожит вселенную. Не нажимайте дважды.
PS Я на самом деле проверил «пересечение потоков», и это работало отлично Я знал, что так и будет. Честный.