Use local database for stop name autocompletion
1 files modified
6 files added
| | |
| | | if(stop_name_autocomplete_xhr) stop_name_autocomplete_xhr.abort(); |
| | | |
| | | stop_name_autocomplete_xhr = $.get( |
| | | ttss_base + '/lookup/autocomplete/json' |
| | | + '?query=' + encodeURIComponent(stop_name.value) |
| | | 'stops.php?query=' + encodeURIComponent(stop_name.value) |
| | | ).done(function(data) { |
| | | deleteChildren(stop_name_autocomplete); |
| | | for(var i = 1, il = data.length; i < il; i++) { |
| | | for(var i = 0, il = data.length; i < il; i++) { |
| | | if(data[i].type != 'stop') continue; |
| | | if(data[i].id > 6000) continue; |
| | | var opt = document.createElement('option'); |
| | | opt.value = data[i].id; |
New file |
| | |
| | | <?php |
| | | include(__DIR__.'/stops/common.php'); |
| | | include(__DIR__.'/stops/stops.php'); |
| | | |
| | | try { |
| | | // Reject invalid input |
| | | if(!isset($_GET['query'])) throw new UnexpectedValueException(); |
| | | if(empty($_GET['query'])) throw new UnexpectedValueException(); |
| | | if(strlen($_GET['query']) > 50) throw new UnexpectedValueException(); |
| | | |
| | | // Initialize DB connection an query |
| | | $pdo = new PDO('sqlite:stops/stops.db', NULL, NULL, array( |
| | | PDO::ATTR_ERRMODE => PDO::ERRMODE_EXCEPTION |
| | | )); |
| | | $st = $pdo->prepare('SELECT DISTINCT id FROM stop_search WHERE word LIKE ?'); |
| | | |
| | | // Split stop name into words |
| | | $words = split_stop_name($_GET['query']); |
| | | |
| | | // Find relevant stop IDs |
| | | $ids = NULL; |
| | | foreach($words as $word) { |
| | | if(empty($word)) continue; |
| | | |
| | | // Find stop IDs with names matching the word |
| | | $st->execute(array($word.'%')); |
| | | $results = $st->fetchAll(PDO::FETCH_COLUMN); |
| | | $st->closeCursor(); |
| | | |
| | | // Merge results with previous searches |
| | | if(is_array($ids)) { |
| | | $ids = array_intersect($ids, $results); |
| | | } else { |
| | | $ids = $results; |
| | | } |
| | | |
| | | // No results will be found |
| | | if(count($ids) == 0) break; |
| | | } |
| | | |
| | | // Close DB connection |
| | | unset($st, $pdo); |
| | | |
| | | // No query was executed |
| | | if(!is_array($ids)) throw new UnexpectedValueException(); |
| | | |
| | | // Build structure for UI |
| | | $stop_list = []; |
| | | foreach($ids as $id) { |
| | | $stop_list[] = [ |
| | | 'id' => $id, |
| | | 'name' => $stops[$id], |
| | | 'type' => 'stop', |
| | | 'relevance' => similar_text($_GET['query'], $stops[$id]) |
| | | ]; |
| | | } |
| | | |
| | | // Sort stops by relevence |
| | | usort($stop_list, function($a, $b) { |
| | | return $b['relevance'] - $a['relevance']; |
| | | }); |
| | | |
| | | // Return JSON |
| | | echo json_encode($stop_list); |
| | | } catch(UnexpectedValueException $e) { |
| | | echo '[]'; |
| | | } catch(Exception $e) { |
| | | header('HTTP/1.1 503 Service Unavailable'); |
| | | echo $e->getMessage(); |
| | | } |
New file |
| | |
| | | <?php |
| | | setlocale(LC_CTYPE, 'pl_PL.UTF-8'); |
| | | |
| | | function split_stop_name($string) { |
| | | $string = strtolower(iconv('utf-8', 'ascii//TRANSLIT', $string)); |
| | | $words = preg_split('/\\W+/', $string); |
| | | |
| | | foreach($words as &$word) { |
| | | $two = substr($word, 0, 2); |
| | | if($two == 'os') $word = 'os'; |
| | | elseif($two == 'al') $word = 'al'; |
| | | elseif($two == 'sw') $word = 'sw'; |
| | | } |
| | | unset($word); |
| | | |
| | | return $words; |
| | | } |
New file |
| | |
| | | <?php |
| | | if(php_sapi_name() !== 'cli') die(); |
| | | |
| | | $chars = 'aąbcćdeęfghijklłmnńoóprsśtuvwxyzżź0123456789'; |
| | | $len = mb_strlen($chars); |
| | | |
| | | $replacements = [ |
| | | 'Ó' => 'Ó', |
| | | 'ó' => 'ó', |
| | | 'É' => 'É', |
| | | 'é' => 'é', |
| | | ]; |
| | | |
| | | $stops = []; |
| | | for($i = 0; $i < $len; $i++) { |
| | | for($j = 0; $j < $len; $j++) { |
| | | $char = mb_substr($chars, $i, 1).mb_substr($chars, $j, 1); |
| | | $json = file_get_contents('http://www.ttss.krakow.pl/internetservice/services/lookup/autocomplete/json?query='.urlencode($char)); |
| | | $elements = json_decode($json, 1); |
| | | foreach($elements as $element) { |
| | | if($element['type'] == 'divider') continue; |
| | | if($element['type'] == 'route') continue; |
| | | if($element['type'] != 'stop') { |
| | | throw new Exception('Unknown element: '.var_export($element, 1)); |
| | | } |
| | | |
| | | $stops[$element['id']] = strtr($element['name'], $replacements); |
| | | } |
| | | } |
| | | } |
| | | |
| | | asort($stops); |
| | | var_export($stops); |
New file |
| | |
| | | <?php |
| | | if(php_sapi_name() !== 'cli') die(); |
| | | |
| | | include('stops.php'); |
| | | include('common.php'); |
| | | |
| | | $pdo = new PDO('sqlite:stops_temp.db'); |
| | | |
| | | $pdo->query('DROP TABLE IF EXISTS stop_search'); |
| | | $pdo->query('CREATE TABLE stop_search ( |
| | | word VARCHAR(60), |
| | | id INT |
| | | )'); |
| | | |
| | | $pdo->beginTransaction(); |
| | | $st = $pdo->prepare('INSERT INTO stop_search (word, id) VALUES (?, ?)'); |
| | | foreach($stops as $id => $name) { |
| | | foreach(split_stop_name($name) as $word) { |
| | | $st->execute(array($word, $id)); |
| | | $st->closeCursor(); |
| | | } |
| | | } |
| | | $pdo->commit(); |
| | | |
| | | $pdo->query('CREATE INDEX stop_search_word ON stop_search (word COLLATE NOCASE)'); |
| | | |
| | | rename('stops_temp.db', 'stops.db'); |
New file |
| | |
| | | <?php |
| | | $stops = array ( |
| | | 113 => 'AWF', |
| | | 462 => 'Agencja Kraków Wschód', |
| | | # 134 => 'Balicka Wiadukt', # nowa nazwa # 136 => 'Bronowice Wiadukt', |
| | | 449 => 'Bardosa', |
| | | # 75 => 'Basztowa LOT', # nowa nazwa # 3032 => 'Stary Kleparz', |
| | | 78 => 'Batorego', |
| | | 130 => 'Białucha', |
| | | 867 => 'Bieńczycka', |
| | | 630 => 'Bieżanowska', |
| | | 84 => 'Biprostal', |
| | | 461 => 'Blokowa', |
| | | # 2798 => 'Boisko Kabel', # dawny tymczasowy |
| | | 747 => 'Borek Fałęcki', |
| | | 824 => 'Borek Fałęcki I', |
| | | 612 => 'Borsucza', |
| | | 451 => 'Brama nr 4', |
| | | 453 => 'Brama nr 5', |
| | | 61 => 'Bratysławska', |
| | | 89 => 'Bronowice', |
| | | 135 => 'Bronowice Małe', |
| | | 136 => 'Bronowice Wiadukt', |
| | | 613 => 'Brożka', |
| | | 409 => 'Centralna', |
| | | 3039 => 'Centrum Kongresowe ICE', |
| | | 2691 => 'Chmieleniec', |
| | | 87 => 'Cichy Kącik', |
| | | 3037 => 'Cienista', # nieczynny |
| | | # 2549 => 'Cmentarz Grębałów Zachód', # nowa nazwa # 2685 => 'Jarzębiny', |
| | | 621 => 'Cmentarz Podgórski', |
| | | 124 => 'Cmentarz Rakowicki', |
| | | 318 => 'Cracovia', |
| | | 129 => 'Cystersów', |
| | | 3038 => 'Czerwone Maki P+R', |
| | | 407 => 'Czyżyny', |
| | | 392 => 'DH Wanda', |
| | | 915 => 'Dajwór', |
| | | 435 => 'Darwina', |
| | | 632 => 'Dauna', |
| | | 388 => 'Dunikowskiego', |
| | | 623 => 'Dworcowa', |
| | | 131 => 'Dworzec Główny', |
| | | 1173 => 'Dworzec Główny Tunel', |
| | | # 8567 => 'Dworzec Główny Tunel', # duplikat # 1173 => 'Dworzec Główny Tunel', |
| | | 2608 => 'Dworzec Główny Zachód', |
| | | 2870 => 'Dworzec Płaszów Estakada', |
| | | 70 => 'Dworzec Towarowy', |
| | | # 6685 => 'Dworzec Towarowy', # duplikat # 70 => 'Dworzec Towarowy', |
| | | 370 => 'Dąbie', |
| | | 464 => 'Elektromontaż', |
| | | 368 => 'Fabryczna', |
| | | 322 => 'Filharmonia', |
| | | 1051 => 'Fort Mogiła', # nieczynny |
| | | 367 => 'Francesco Nullo', |
| | | 560 => 'Gromadzka', |
| | | # 585 => 'Grota Roweckiego', # nowa nazwa # 2687 => 'Grota-Roweckiego', |
| | | 2687 => 'Grota-Roweckiego', |
| | | 1049 => 'Głowackiego', |
| | | 363 => 'Hala Targowa', |
| | | # 6990 => 'Hala Targowa', # duplikat # 363 => 'Hala Targowa', |
| | | 2685 => 'Jarzębiny', |
| | | 452 => 'Jeżynowa', |
| | | 319 => 'Jubilat', |
| | | 624 => 'Kabel', |
| | | 2690 => 'Kampus UJ', |
| | | 576 => 'Kapelanka', |
| | | 429 => 'Klasztorna', |
| | | 382 => 'Kleeberga', |
| | | 946 => 'Klimeckiego', |
| | | 584 => 'Kobierzyńska', |
| | | # 401 => 'Kocmyrzowska', # nowa nazwa # 3037 => 'Cienista', |
| | | 457 => 'Koksochemia', |
| | | 459 => 'Kombinat', |
| | | 313 => 'Komorowskiego', |
| | | 450 => 'Kopiec Wandy', |
| | | # 2536 => 'Kordylewskiego', # nowa nazwa # 2859 => 'Teatr Variété', |
| | | 571 => 'Korona', |
| | | # 2803 => 'Kraków Arena Al. Pokoju', # nowa nazwa # 2871 => 'TAURON Arena Kraków Al. Pokoju', |
| | | # 959 => 'Kraków Plaza', # nowa nazwa # 3033 => 'Plaza', |
| | | 63 => 'Krowodrza Górka', |
| | | # 7612 => 'Krowodrza Górka', # duplikat # 63 => 'Krowodrza Górka', |
| | | 567 => 'Kuklińskiego', |
| | | 744 => 'Kurdwanów', |
| | | # 7389 => 'Kurdwanów pętla', # nowa nazwa # 744 => 'Kurdwanów', |
| | | # 2537 => 'Lema', # nowa nazwa # 2803 => 'Kraków Arena Al. Pokoju' |
| | | 569 => 'Limanowskiego', |
| | | 2686 => 'Lipińskiego', |
| | | 561 => 'Lipska', |
| | | 126 => 'Lubicz', |
| | | 930 => 'M1 Al. Pokoju', |
| | | 1263 => 'Mały Płaszów', |
| | | 454 => 'Meksyk', |
| | | 362 => 'Miodowa', |
| | | # 6989 => 'Miodowa', # duplikat # 362 => 'Miodowa', |
| | | 375 => 'Mistrzejowice', |
| | | 2538 => 'Miśnieńska', |
| | | # 574 => 'Most Grunwaldzki', # nowa nazwa # 3039 => 'Centrum Kongresowe ICE', |
| | | 460 => 'Mrozowa', |
| | | 2726 => 'Muzeum Inżynierii Miejskiej', |
| | | 2811 => 'Muzeum Lotnictwa', |
| | | 2688 => 'Norymberska', |
| | | # 372 => 'Nowohucka', # nowa nazwa # 3041 => 'Rondo 308. Dywizjonu', |
| | | 715 => 'Nowosądecka', |
| | | 2580 => 'Nowy Bieżanów', |
| | | 71 => 'Nowy Kleparz', |
| | | 2582 => 'Nowy Prokocim', |
| | | 369 => 'Ofiar Dąbia', |
| | | 823 => 'Oleandry', |
| | | 361 => 'Orzeszkowej', |
| | | 413 => 'Os.Kolorowe', |
| | | 424 => 'Os.Na Skarpie', |
| | | 378 => 'Os.Piastów', |
| | | 418 => 'Os.Zgody', |
| | | 377 => 'Os.Złotego Wieku', |
| | | 466 => 'PH', |
| | | 614 => 'PT', |
| | | 960 => 'Park Jordana', |
| | | 716 => 'Piaski Nowe', |
| | | 379 => 'Piasta Kołodzieja', |
| | | 570 => 'Plac Bohaterów Getta', |
| | | # 7207 => 'Plac Bohaterów Getta', # duplikat # 570 => 'Plac Bohaterów Getta', |
| | | # 414 => 'Plac Centralny', # nowa nazwa # 2744 => 'Plac Centralny im. R.Reagana', |
| | | 2744 => 'Plac Centralny im. R.Reagana', |
| | | 79 => 'Plac Inwalidów', |
| | | 360 => 'Plac Wolnica', |
| | | 1360 => 'Plac Wszystkich Świętych', |
| | | 3033 => 'Plaza', |
| | | 458 => 'Pleszów', |
| | | 357 => 'Poczta Główna', |
| | | 73 => 'Politechnika', |
| | | # 6689 => 'Politechnika', # duplikat # 73 => 'Politechnika', |
| | | 568 => 'Powstańców Wielkopolskich', |
| | | # 7204 => 'Powstańców Wielkopolskich', # duplikat # 568 => 'Powstańców Wielkopolskich', |
| | | 637 => 'Prokocim', |
| | | # 7279 => 'Prokocim', # duplikat # 637 => 'Prokocim', |
| | | 682 => 'Prokocim Szpital', |
| | | # 69 => 'Prądnicka', # nowa nazwa # 3036 => 'Szpital Narutowicza', |
| | | # 6684 => 'Prądnicka', # duplikat # 69 => 'Prądnicka', |
| | | 72 => 'Pędzichów', |
| | | 128 => 'Rakowicka', |
| | | 320 => 'Reymana', |
| | | 3041 => 'Rondo 308. Dywizjonu', |
| | | 408 => 'Rondo Czyżyńskie', |
| | | 365 => 'Rondo Grzegórzeckie', |
| | | # 6992 => 'Rondo Grzegórzeckie', # duplikat # 365 => 'Rondo Grzegórzeckie', |
| | | 2539 => 'Rondo Hipokratesa', |
| | | # 419 => 'Rondo Kocmyrzowskie', # nowa nazwa # 2745 => 'Rondo Kocmyrzowskie im. Ks. Gorzelanego', |
| | | 2745 => 'Rondo Kocmyrzowskie im. Ks. Gorzelanego', |
| | | 610 => 'Rondo Matecznego', |
| | | 125 => 'Rondo Mogilskie', |
| | | # 6747 => 'Rondo Mogilskie', # duplikat # 125 => 'Rondo Mogilskie', |
| | | 383 => 'Rondo Piastowskie', |
| | | # 587 => 'Rostworowskiego', # nowa nazwa # 589 => 'Ruczaj', |
| | | 589 => 'Ruczaj', |
| | | # 586 => 'Ruczaj I', # nowa nazwa # 585 => 'Grota Roweckiego', |
| | | 1262 => 'Rzebika', |
| | | 611 => 'Rzemieślnicza', |
| | | 311 => 'Salwator', |
| | | 615 => 'Sanktuarium Bożego Miłosierdzia', |
| | | 572 => 'Smolki', |
| | | 746 => 'Solvay', |
| | | 358 => 'Starowiślna', |
| | | # 6985 => 'Starowiślna', # duplikat # 358 => 'Starowiślna', |
| | | 3032 => 'Stary Kleparz', |
| | | 112 => 'Stella-Sawickiego', |
| | | 359 => 'Stradom', |
| | | 423 => 'Struga', |
| | | 2548 => 'Suche Stawy', |
| | | 3036 => 'Szpital Narutowicza', |
| | | 575 => 'Szwedzka', |
| | | 577 => 'Słomiana', |
| | | 2871 => 'TAURON Arena Kraków Al. Pokoju', |
| | | 3040 => 'TAURON Arena Kraków Wieczysta', |
| | | 77 => 'Teatr Bagatela', |
| | | 420 => 'Teatr Ludowy', # nieczynny |
| | | 2859 => 'Teatr Variété', |
| | | 681 => 'Teligi', |
| | | 127 => 'Uniwersytet Ekonomiczny', |
| | | 321 => 'Uniwersytet Jagielloński', |
| | | 88 => 'Uniwersytet Pedagogiczny', |
| | | 83 => 'Urzędnicza', |
| | | 463 => 'Walcownia', |
| | | 325 => 'Wawel', |
| | | 2543 => 'Wańkowicza', # nieczynny |
| | | 133 => 'Wesele', |
| | | 434 => 'Wiadukty', |
| | | # 114 => 'Wieczysta', # nowa nazwa # 3040 => 'TAURON Arena Kraków Wieczysta', |
| | | 718 => 'Witosa', |
| | | # 7362 => 'Witosa', # duplikat # 718 => 'Witosa', |
| | | 634 => 'Wlotowa', |
| | | 1154 => 'Zabłocie', |
| | | 465 => 'Zajezdnia Nowa Huta', |
| | | 679 => 'Ćwiklińskiej', |
| | | 922 => 'Łagiewniki', |
| | | # 7584 => 'Łagiewniki', # duplikat # 922 => 'Łagiewniki', |
| | | 2821 => 'Łagiewniki ZUS', |
| | | # 324 => 'Św. Gertrudy', # duplikat # 2741 => 'Św.Gertrudy', |
| | | # 364 => 'Św. Wawrzyńca', # duplikat # 2742 => 'Św.Wawrzyńca', |
| | | # 8508 => 'Św. Wawrzyńca', # duplikat # 364 => 'Św. Wawrzyńca', |
| | | 2741 => 'Św.Gertrudy', |
| | | 2742 => 'Św.Wawrzyńca', |
| | | ); |