Постоянный участник клуба разработчиков
Ранее мы уже публиковали класс множественного геокодирования на клиенте и наш вариант получения координат для списка адресов с использованием PHP и MySQL.
Спасибо автору за еще одну реализацию!
Серверная реализация мультигеокодирования
в одном проектике была поставлена задача: геокодировать более 1000 адресов из загружаемого excel-файла с адресами.
алгоритм такой:
1. загрузка excel-файла и его парсинг в csv
2. вывод окна с формой ajax, которая опрашивает сервер каждую минуту до окончания геокодирования
3. если юзер обновляет окно, сервер проверяет наличие "недокодированного" файла с данными,и, если такой есть, показывает форму из п.2
4. по окончании геокодирования выводится список ошибок и файл с данными удаляется.
вот функция собственно геокодирования:
$result = array('html'=>'');
// проверяем существовование файла с данными для геокодированияif (file_exists($dataraw)) {// инициализируем memcache$memcache = new Memcache;$memcache->pconnect('127.0.0.1', 11211);// грузим данные для геокодирования$data = file($dataraw, FILE_IGNORE_NEW_LINES);// устанавливаем таймаут работы скрипта$endtime = time() + 60;$storage = '';$count = 0;$geocoded = 0;$errordata = '';// инициализируем curl.$ch = curl_init();curl_setopt($ch, CURLOPT_HEADER, 0);curl_setopt($ch, CURLOPT_TIMEOUT, 10);curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);// цикл геокодирования заканчивается по таймаут либо когда массив с адресами кончитсяwhile ((time() < $endtime) && sizeof($data)) {$count ++;$coords = false;$string = array_shift($data);$row = str_getcsv($string);array_walk($row, create_function('&$val', '$val = trim($val);'));// тут превращаем часть столбцов массива данных в строку адреса$addr = implode(',', array_slice($row, 4, 4));// хэш для хранения и поиска в memcache$hash = md5($addr);// проверяем наличие уже отгеокодированного адреса в memcache$coords = $memcache->get($hash);// обраращение к геокодеруif ($coords === false){curl_setopt($ch, CURLOPT_URL, 'http://geocode-maps.yandex.ru/1.x/?kind=house'.'&geocode='.urlencode('Россия,+'.implode(',+', array_slice($row, 4, 4))));$content = curl_exec($ch);if (curl_error($ch)) {break;}$xml = simplexml_load_file($content);// тут коряво, конечно, но проверяем точность геокодирования. нас вполне устроит точность exact, number, near и streetif ($xml->GeoObjectCollection->metaDataProperty->Geoc oderResponseMetaData->fou nd !=0 && in_array($xml->GeoObjectCollection->featureMember[ 0]->GeoObject->metaDataPr operty->GeocoderMetaData- >precision, array('exact', 'number','near', 'street'))) {// сохраняем в memcache если геокодирование прошло удачно$coords = sprintf($xml->GeoObjectCollection->featureMember[0 ]->GeoObject->Point->pos[ 0]); $memcache->set($hash, $coords, MEMCACHE_COMPRESSED, strtotime('+30 days'));} else {// сохраняем false, если геокодирование прошло неудачно.$memcache->set($hash, NULL, MEMCACHE_COMPRESSED, strtotime('+1 days'));}$geocoded ++;};if ($coords) {// сохраняем результатlist($lng, $lat) = explode(' ', $coords);$storage .= ($storage ? '|' : '') . implode(":", array($row[0],$lat,$lng,isset($row[9]) ? intval($row[9]) : 0,$row[0].". ".$row[1].' '.$row[2].' '.$row[3].'<br />'. implode(',', array_slice($row, 4, 5))));} else {// или сохраняем ошибку$errordata .= "<tr><td>{$row[0]}</td><td>$addr</td></tr>";};};// сохраняем ошибки в файлfile_put_contents($error, $errordata."<tr colspan='2'>Обработано $count, геокодировано $geocoded<td></tr>");// если отработан весь файл с данными - удаляем его и выводим ошибкиif (sizeof($data) == 0) {@unlink($dataraw);$result['done'] = true;file_put_contents($error, "<tr><td colspan='2'>Обработка завершена<td></tr>" ."<tr><td colspan='2'><a href='/mapexcel/'>Нажмите для переходы к карте</a><td></tr>", FILE_APPEND);} else {file_put_contents($error, "<tr><td colspan='2'>Обработано $count. Осталось ".sizeof($data)."<td></tr>", FILE_APPEND);};$result['html'] = file_exists($error) ? file_get_contents($error) : '<tr><td colspan="2">Ошибок не найдено</td></tr>';// сохраняем файл с обработанными даннымиfile_put_contents($datafile, $storage);// отправляем ответ jsonheader('Content-Type: application/json');echo json_encode($result);exit;}
клиентская частьif (file_exists($dataraw)){echo '<h1>Обработка загруженного файла</h1>';echo '<div id="result">Обрабатывается...</div>'.PHP_EOL;echo '<script type="text/javascript">$().ready(function() {var load = function() {$.get("?action=work", function(response) {$("#result").html(response.html);if (!response.done) {window.setTimeout(load, 1000);}});};window.setTimeout(load, 1000);})</script></body></html>';exit;};
пояснения:
1. cron не использую, так как проект мелкий, да и потом вспомнить, что там было в кроне - сложно
2. почему без mysql? нет смысла морочиться из-за такого мелкого объема. данные обновляются ежедневно, а вот адресов будет 20-50 новых в день
3. memcache? вместо него можно использовать что угодно - mysql, файловый кэш и т.д. (сложнее, медленнее)
4. почему curl? сначала исползовал file_get_contents, но после большого числа обращений к геокодеру, яндекс начинает отдавать результаты с большой задержкой и скрипт вылетает по таймауту. Можно поставить set_time_limit(0), но все расвно вылетает из-за Nginx. в общем, curl хорош тем, что легко настраивается timeout. Кстати, насиловать яндекс тысячами адресов в минуту тоже нехорошо)