2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | AJAX Example(by jQuery): search locations by Ajax request
17 |
18 |
19 |
20 | Search values:
21 | black, blue, cyan, darkblue, darkred, darkgray, gray, gree, red, yellow, white
22 |
23 |
24 |
25 |
26 |
27 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
--------------------------------------------------------------------------------
/examples/ajax.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | AJAX Example: search locations by Ajax request, with custom icon marker
15 |
16 |
17 |
18 | Search values:
19 | black, blue, cyan, darkblue, darkred, darkgray, gray, gree, red, yellow, white
20 |
21 |
22 |
23 |
24 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
--------------------------------------------------------------------------------
/examples/custom-source-data.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Static data from custom data source Example: search locations using option sourceData in static Array
15 |
16 |
17 |
18 | Search values:
19 | black, blue, cyan, darkblue, darkred, darkgray, gray, gree, red, yellow, white
20 |
21 |
22 |
23 |
24 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/examples/custom-tip.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Custom Tip Example: customize each tip in menu
15 |
16 |
17 |
18 | Search values:
19 | aquamarine, black, blue, cyan, darkblue, darkred, darkgray, dodgerblue, gray, green, red, skyblue, yellow, white ...
20 |
21 |
22 |
23 |
24 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/examples/data/bar.geojson.js:
--------------------------------------------------------------------------------
1 | var bar = {
2 | "type": "FeatureCollection",
3 | "generator": "overpass-turbo",
4 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.",
5 | "timestamp": "2015-08-08T19:03:02Z",
6 | "features": [
7 | {
8 | "type": "Feature",
9 | "id": "node/500129236",
10 | "properties": {
11 | "@id": "node/500129236",
12 | "addr:city": "Roma",
13 | "addr:country": "IT",
14 | "addr:housenumber": "5",
15 | "addr:postcode": "00184",
16 | "addr:street": "Piazza della Madonna dei Monti",
17 | "amenity": "bar",
18 | "name": "la Bottega del caffe'"
19 | },
20 | "geometry": {
21 | "type": "Point",
22 | "coordinates": [
23 | 12.4910927,
24 | 41.8950196
25 | ]
26 | }
27 | },
28 | {
29 | "type": "Feature",
30 | "id": "node/560446888",
31 | "properties": {
32 | "@id": "node/560446888",
33 | "amenity": "bar",
34 | "name": "Marani"
35 | },
36 | "geometry": {
37 | "type": "Point",
38 | "coordinates": [
39 | 12.5132952,
40 | 41.8970607
41 | ]
42 | }
43 | },
44 | {
45 | "type": "Feature",
46 | "id": "node/574326909",
47 | "properties": {
48 | "@id": "node/574326909",
49 | "amenity": "bar",
50 | "name": "Il Faraone"
51 | },
52 | "geometry": {
53 | "type": "Point",
54 | "coordinates": [
55 | 12.4910603,
56 | 41.8948257
57 | ]
58 | }
59 | },
60 | {
61 | "type": "Feature",
62 | "id": "node/1213332726",
63 | "properties": {
64 | "@id": "node/1213332726",
65 | "addr:city": "Roma",
66 | "addr:country": "IT",
67 | "addr:housenumber": "139",
68 | "addr:postcode": "00185",
69 | "addr:street": "Via Principe Amedeo",
70 | "amenity": "bar",
71 | "name": "Snackbar Eugenio",
72 | "operator": "Marchetti Eugenio",
73 | "phone": "+39 06 4466154"
74 | },
75 | "geometry": {
76 | "type": "Point",
77 | "coordinates": [
78 | 12.5031364,
79 | 41.8970108
80 | ]
81 | }
82 | },
83 | {
84 | "type": "Feature",
85 | "id": "node/1660369493",
86 | "properties": {
87 | "@id": "node/1660369493",
88 | "addr:city": "Roma",
89 | "addr:country": "IT",
90 | "addr:housenumber": "27",
91 | "addr:street": "Via San Martino della Battaglia",
92 | "amenity": "bar",
93 | "name": "Pretorius Cafe'",
94 | "operator": "Cesarina Utilia",
95 | "phone": "+39 06 490077",
96 | "ref:vatin": "IT09397401002"
97 | },
98 | "geometry": {
99 | "type": "Point",
100 | "coordinates": [
101 | 12.5051946,
102 | 41.905864
103 | ]
104 | }
105 | },
106 | {
107 | "type": "Feature",
108 | "id": "node/1660404822",
109 | "properties": {
110 | "@id": "node/1660404822",
111 | "addr:city": "Roma",
112 | "addr:country": "IT",
113 | "addr:housenumber": "9",
114 | "addr:street": "Via Clementina",
115 | "amenity": "bar",
116 | "cuisine": "regional",
117 | "name": "Casa Clementina",
118 | "operator": "Flexy Soc.Coop Arl.",
119 | "phone": "+39 06 48913254",
120 | "ref:vatin": "IT09253451000",
121 | "restaurant:type": "enoteca"
122 | },
123 | "geometry": {
124 | "type": "Point",
125 | "coordinates": [
126 | 12.492714,
127 | 41.8959687
128 | ]
129 | }
130 | },
131 | {
132 | "type": "Feature",
133 | "id": "node/1926196463",
134 | "properties": {
135 | "@id": "node/1926196463",
136 | "amenity": "bar",
137 | "name": "Twins"
138 | },
139 | "geometry": {
140 | "type": "Point",
141 | "coordinates": [
142 | 12.5009894,
143 | 41.8999812
144 | ]
145 | }
146 | },
147 | {
148 | "type": "Feature",
149 | "id": "node/2092013187",
150 | "properties": {
151 | "@id": "node/2092013187",
152 | "addr:housenumber": "12",
153 | "addr:street": "Via di San Giovanni in Laterano",
154 | "amenity": "bar",
155 | "name": "My Bar",
156 | "operator": "Anfiteatro S.r.l.",
157 | "phone": "+39 06 7004425",
158 | "ref:vatin": "IT067475410008"
159 | },
160 | "geometry": {
161 | "type": "Point",
162 | "coordinates": [
163 | 12.4946166,
164 | 41.8898973
165 | ]
166 | }
167 | },
168 | {
169 | "type": "Feature",
170 | "id": "node/2128214065",
171 | "properties": {
172 | "@id": "node/2128214065",
173 | "addr:city": "Roma",
174 | "addr:country": "IT",
175 | "addr:housenumber": "106",
176 | "addr:postcode": "00184",
177 | "addr:street": "Via di San Giovanni in Laterano",
178 | "amenity": "bar",
179 | "cuisine": "italian",
180 | "name": "San Clemente",
181 | "source": "GPS",
182 | "website": "http://www.iclementini.it/"
183 | },
184 | "geometry": {
185 | "type": "Point",
186 | "coordinates": [
187 | 12.4979123,
188 | 41.8889233
189 | ]
190 | }
191 | },
192 | {
193 | "type": "Feature",
194 | "id": "node/2244964954",
195 | "properties": {
196 | "@id": "node/2244964954",
197 | "addr:housenumber": "40",
198 | "addr:street": "Via Palestro",
199 | "amenity": "bar",
200 | "contact:phone": "+39 06 49382682",
201 | "contact:website": "http://the-yellow.com/",
202 | "name": "The Yellow Bar",
203 | "payment:bitcoin": "yes"
204 | },
205 | "geometry": {
206 | "type": "Point",
207 | "coordinates": [
208 | 12.5045633,
209 | 41.9048511
210 | ]
211 | }
212 | },
213 | {
214 | "type": "Feature",
215 | "id": "node/2361780469",
216 | "properties": {
217 | "@id": "node/2361780469",
218 | "addr:city": "Roma",
219 | "addr:housenumber": "72/73",
220 | "addr:street": "Via Urbana",
221 | "amenity": "bar",
222 | "name": "Er Caffettiere",
223 | "operator": "Sa.Pe. SNC di Spartaco Pelle & C.",
224 | "phone": "+39 320 4168532",
225 | "ref:vatin": "IT09487221005",
226 | "restaurant:type:it": "bar;tavola_calda"
227 | },
228 | "geometry": {
229 | "type": "Point",
230 | "coordinates": [
231 | 12.4929144,
232 | 41.8949856
233 | ]
234 | }
235 | },
236 | {
237 | "type": "Feature",
238 | "id": "node/3217327590",
239 | "properties": {
240 | "@id": "node/3217327590",
241 | "addr:city": "Roma",
242 | "addr:housenumber": "65",
243 | "addr:street": "Via dei Cerchi",
244 | "amenity": "bar",
245 | "name": "0,75",
246 | "opening_hours": "Mo-Su 11:00-02:00",
247 | "phone": "+39 066875706"
248 | },
249 | "geometry": {
250 | "type": "Point",
251 | "coordinates": [
252 | 12.4839357,
253 | 41.8879519
254 | ]
255 | }
256 | },
257 | {
258 | "type": "Feature",
259 | "id": "node/3320437843",
260 | "properties": {
261 | "@id": "node/3320437843",
262 | "addr:housenumber": "26",
263 | "addr:street": "Via Capo d'Africa",
264 | "amenity": "bar",
265 | "name": "La Follia",
266 | "operator": "Dolce Calabria SAS di Morello Alessandra & C.",
267 | "phone": "+39 06 45477438",
268 | "ref:vatin": "IT11279721002",
269 | "restaurant:type:it": "wine_bar",
270 | "shop": "pastry"
271 | },
272 | "geometry": {
273 | "type": "Point",
274 | "coordinates": [
275 | 12.4952573,
276 | 41.8888415
277 | ]
278 | }
279 | },
280 | {
281 | "type": "Feature",
282 | "id": "node/3403821230",
283 | "properties": {
284 | "@id": "node/3403821230",
285 | "amenity": "bar",
286 | "name": "Snack bar peter bar"
287 | },
288 | "geometry": {
289 | "type": "Point",
290 | "coordinates": [
291 | 12.4978786,
292 | 41.9001486
293 | ]
294 | }
295 | },
296 | {
297 | "type": "Feature",
298 | "id": "node/3485963126",
299 | "properties": {
300 | "@id": "node/3485963126",
301 | "addr:housename": "Roma Termini",
302 | "addr:housenumber": "25",
303 | "addr:street": "Via Marsala",
304 | "amenity": "bar",
305 | "brand": "Chef Express",
306 | "building:level": "0",
307 | "name": "Buffet Roma Termini",
308 | "operator": "Chef Express S.p.A.",
309 | "ref:vatin": "IT00876120213"
310 | },
311 | "geometry": {
312 | "type": "Point",
313 | "coordinates": [
314 | 12.5023541,
315 | 41.9016306
316 | ]
317 | }
318 | },
319 | {
320 | "type": "Feature",
321 | "id": "node/3551307620",
322 | "properties": {
323 | "@id": "node/3551307620",
324 | "addr:housenumber": "30-34",
325 | "addr:street": "Via Ludovisi",
326 | "amenity": "bar",
327 | "name": "Bar Ludovisi",
328 | "operator": "Doppio Zero Srl.",
329 | "phone": "+39 06 42016014",
330 | "ref:vatin": "IT11344331001"
331 | },
332 | "geometry": {
333 | "type": "Point",
334 | "coordinates": [
335 | 12.4887487,
336 | 41.9069283
337 | ]
338 | }
339 | },
340 | {
341 | "type": "Feature",
342 | "id": "node/3578353919",
343 | "properties": {
344 | "@id": "node/3578353919",
345 | "addr:city": "Roma",
346 | "addr:country": "IT",
347 | "addr:housenumber": "36/37",
348 | "addr:postcode": "00185",
349 | "addr:street": "Via Merulana",
350 | "amenity": "bar",
351 | "name": "Il Pasticciaccio",
352 | "operator": "Angelo Blu s.r.l.",
353 | "phone": "+39 06 4826928",
354 | "ref:vatin": "IT09547101007"
355 | },
356 | "geometry": {
357 | "type": "Point",
358 | "coordinates": [
359 | 12.5005788,
360 | 41.8948307
361 | ]
362 | }
363 | },
364 | {
365 | "type": "Feature",
366 | "id": "node/3607603463",
367 | "properties": {
368 | "@id": "node/3607603463",
369 | "addr:city": "Roma",
370 | "addr:housenumber": "22",
371 | "addr:postcode": "00185",
372 | "addr:street": "Via Tiburtina",
373 | "amenity": "bar",
374 | "name": "Buddha Bar"
375 | },
376 | "geometry": {
377 | "type": "Point",
378 | "coordinates": [
379 | 12.5121332,
380 | 41.8971197
381 | ]
382 | }
383 | }
384 | ]
385 | };
--------------------------------------------------------------------------------
/examples/data/colors.js:
--------------------------------------------------------------------------------
1 | define(function() {
2 | return [
3 | {"loc":[41.575330,13.102411], "title":"aquamarine"},
4 | {"loc":[41.575730,13.002411], "title":"black"},
5 | {"loc":[41.807149,13.162994], "title":"blue"},
6 | {"loc":[41.507149,13.172994], "title":"chocolate"},
7 | {"loc":[41.847149,14.132994], "title":"coral"},
8 | {"loc":[41.219190,13.062145], "title":"cyan"},
9 | {"loc":[41.344190,13.242145], "title":"darkblue"},
10 | {"loc":[41.679190,13.122145], "title":"Darkred"},
11 | {"loc":[41.329190,13.192145], "title":"Darkgray"},
12 | {"loc":[41.379290,13.122545], "title":"dodgerblue"},
13 | {"loc":[41.409190,13.362145], "title":"gray"},
14 | {"loc":[41.794008,12.583884], "title":"green"},
15 | {"loc":[41.805008,12.982884], "title":"greenyellow"},
16 | {"loc":[41.536175,13.273590], "title":"red"},
17 | {"loc":[41.516175,13.373590], "title":"rosybrown"},
18 | {"loc":[41.506175,13.273590], "title":"royalblue"},
19 | {"loc":[41.836175,13.673590], "title":"salmon"},
20 | {"loc":[41.796175,13.570590], "title":"seagreen"},
21 | {"loc":[41.436175,13.573590], "title":"seashell"},
22 | {"loc":[41.336175,13.973590], "title":"silver"},
23 | {"loc":[41.236175,13.273590], "title":"skyblue"},
24 | {"loc":[41.546175,13.473590], "title":"yellow"},
25 | {"loc":[41.239190,13.032145], "title":"white"}
26 | ];
27 | });
--------------------------------------------------------------------------------
/examples/data/csv2json.php:
--------------------------------------------------------------------------------
1 | $row[1], 'loc'=>array( (float)$row[4], (float)$row[5]) );
28 | $n++;
29 | }
30 | }
31 | fclose($csvFile);
32 | }
33 |
34 | echo "generated $n records\n";
35 |
36 | $json = json_encode($data);
37 |
38 | file_put_contents('cities15000.json', $json);
39 |
40 | ?>
41 |
--------------------------------------------------------------------------------
/examples/data/custom-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/examples/data/custom-icon.png
--------------------------------------------------------------------------------
/examples/data/htmlcolors.txt:
--------------------------------------------------------------------------------
1 | aliceblue:#f0f8ff
2 | antiquewhite:#faebd7
3 | aqua:#00ffff
4 | aquamarine:#7fffd4
5 | azure:#f0ffff
6 | beige:#f5f5dc
7 | bisque:#ffe4c4
8 | black:#000000
9 | blanchedalmond:#ffebcd
10 | blue:#0000ff
11 | blueviolet:#8a2be2
12 | brown:#a52a2a
13 | burlywood:#deb887
14 | cadetblue:#5f9ea0
15 | chartreuse:#7fff00
16 | chocolate:#d2691e
17 | coral:#ff7f50
18 | cornflowerblue:#6495ed
19 | cornsilk:#fff8dc
20 | crimson:#dc143c
21 | cyan:#00ffff
22 | darkblue:#00008b
23 | darkcyan:#008b8b
24 | darkgoldenrod:#b8860b
25 | darkgray:#a9a9a9
26 | darkgrey:#a9a9a9
27 | darkgreen:#006400
28 | darkkhaki:#bdb76b
29 | darkmagenta:#8b008b
30 | darkolivegreen:#556b2f
31 | darkorange:#ff8c00
32 | darkorchid:#9932cc
33 | darkred:#8b0000
34 | darksalmon:#e9967a
35 | darkseagreen:#8fbc8f
36 | darkslateblue:#483d8b
37 | darkslategray:#2f4f4f
38 | darkslategrey:#2f4f4f
39 | darkturquoise:#00ced1
40 | darkviolet:#9400d3
41 | deeppink:#ff1493
42 | deepskyblue:#00bfff
43 | dimgray:#696969
44 | dimgrey:#696969
45 | dodgerblue:#1e90ff
46 | firebrick:#b22222
47 | floralwhite:#fffaf0
48 | forestgreen:#228b22
49 | fuchsia:#ff00ff
50 | gainsboro:#dcdcdc
51 | ghostwhite:#f8f8ff
52 | gold:#ffd700
53 | goldenrod:#daa520
54 | gray:#808080
55 | grey:#808080
56 | green:#008000
57 | greenyellow:#adff2f
58 | honeydew:#f0fff0
59 | hotpink:#ff69b4
60 | indianred :#cd5c5c
61 | indigo :#4b0082
62 | ivory:#fffff0
63 | khaki:#f0e68c
64 | lavender:#e6e6fa
65 | lavenderblush:#fff0f5
66 | lawngreen:#7cfc00
67 | lemonchiffon:#fffacd
68 | lightblue:#add8e6
69 | lightcoral:#f08080
70 | lightcyan:#e0ffff
71 | lightgoldenrodyellow:#fafad2
72 | lightgray:#d3d3d3
73 | lightgrey:#d3d3d3
74 | lightgreen:#90ee90
75 | lightpink:#ffb6c1
76 | lightsalmon:#ffa07a
77 | lightseagreen:#20b2aa
78 | lightskyblue:#87cefa
79 | lightslategray:#778899
80 | lightslategrey:#778899
81 | lightsteelblue:#b0c4de
82 | lightyellow:#ffffe0
83 | lime:#00ff00
84 | limegreen:#32cd32
85 | linen:#faf0e6
86 | magenta:#ff00ff
87 | maroon:#800000
88 | mediumaquamarine:#66cdaa
89 | mediumblue:#0000cd
90 | mediumorchid:#ba55d3
91 | mediumpurple:#9370db
92 | mediumseagreen:#3cb371
93 | mediumslateblue:#7b68ee
94 | mediumspringgreen:#00fa9a
95 | mediumturquoise:#48d1cc
96 | mediumvioletred:#c71585
97 | midnightblue:#191970
98 | mintcream:#f5fffa
99 | mistyrose:#ffe4e1
100 | moccasin:#ffe4b5
101 | navajowhite:#ffdead
102 | navy:#000080
103 | oldlace:#fdf5e6
104 | olive:#808000
105 | olivedrab:#6b8e23
106 | orange:#ffa500
107 | orangered:#ff4500
108 | orchid:#da70d6
109 | palegoldenrod:#eee8aa
110 | palegreen:#98fb98
111 | paleturquoise:#afeeee
112 | palevioletred:#db7093
113 | papayawhip:#ffefd5
114 | peachpuff:#ffdab9
115 | peru:#cd853f
116 | pink:#ffc0cb
117 | plum:#dda0dd
118 | powderblue:#b0e0e6
119 | purple:#800080
120 | red:#ff0000
121 | rosybrown:#bc8f8f
122 | royalblue:#4169e1
123 | saddlebrown:#8b4513
124 | salmon:#fa8072
125 | sandybrown:#f4a460
126 | seagreen:#2e8b57
127 | seashell:#fff5ee
128 | sienna:#a0522d
129 | silver:#c0c0c0
130 | skyblue:#87ceeb
131 | slateblue:#6a5acd
132 | slategray:#708090
133 | slategrey:#708090
134 | snow:#fffafa
135 | springgreen:#00ff7f
136 | steelblue:#4682b4
137 | tan:#d2b48c
138 | teal:#008080
139 | thistle:#d8bfd8
140 | tomato:#ff6347
141 | turquoise:#40e0d0
142 | violet:#ee82ee
143 | wheat:#f5deb3
144 | white:#ffffff
145 | whitesmoke:#f5f5f5
146 | yellow:#ffff00
147 | yellowgreen:#9acd32
148 |
--------------------------------------------------------------------------------
/examples/data/pharmacy.geojson.js:
--------------------------------------------------------------------------------
1 | var pharmacy = {
2 | "type": "FeatureCollection",
3 | "generator": "overpass-turbo",
4 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.",
5 | "timestamp": "2015-08-08T19:03:02Z",
6 | "features": [
7 | {
8 | "type": "Feature",
9 | "id": "node/256572195",
10 | "properties": {
11 | "@id": "node/256572195",
12 | "addr:city": "Roma",
13 | "addr:country": "IT",
14 | "addr:housenumber": "35",
15 | "addr:postcode": "00185",
16 | "addr:street": "Piazza Manfredo Fanti",
17 | "amenity": "pharmacy",
18 | "dispensing": "yes",
19 | "name": "Farmacia Ricci"
20 | },
21 | "geometry": {
22 | "type": "Point",
23 | "coordinates": [
24 | 12.5020975,
25 | 41.8976328
26 | ]
27 | }
28 | },
29 | {
30 | "type": "Feature",
31 | "id": "node/256592933",
32 | "properties": {
33 | "@id": "node/256592933",
34 | "amenity": "pharmacy",
35 | "created_by": "JOSM",
36 | "dispensing": "yes",
37 | "name": "P. Eugenio"
38 | },
39 | "geometry": {
40 | "type": "Point",
41 | "coordinates": [
42 | 12.50783,
43 | 41.8939352
44 | ]
45 | }
46 | },
47 | {
48 | "type": "Feature",
49 | "id": "node/256592935",
50 | "properties": {
51 | "@id": "node/256592935",
52 | "amenity": "pharmacy",
53 | "created_by": "JOSM",
54 | "dispensing": "yes",
55 | "name": "Parafarmaciapiu'"
56 | },
57 | "geometry": {
58 | "type": "Point",
59 | "coordinates": [
60 | 12.5106824,
61 | 41.8930159
62 | ]
63 | }
64 | },
65 | {
66 | "type": "Feature",
67 | "id": "node/256752359",
68 | "properties": {
69 | "@id": "node/256752359",
70 | "amenity": "pharmacy",
71 | "dispensing": "yes",
72 | "name": "De Sanctis"
73 | },
74 | "geometry": {
75 | "type": "Point",
76 | "coordinates": [
77 | 12.5057196,
78 | 41.8926807
79 | ]
80 | }
81 | },
82 | {
83 | "type": "Feature",
84 | "id": "node/256752364",
85 | "properties": {
86 | "@id": "node/256752364",
87 | "amenity": "pharmacy",
88 | "dispensing": "yes",
89 | "name": "Trecca Mastrangeli"
90 | },
91 | "geometry": {
92 | "type": "Point",
93 | "coordinates": [
94 | 12.5063628,
95 | 41.8906915
96 | ]
97 | }
98 | },
99 | {
100 | "type": "Feature",
101 | "id": "node/256752379",
102 | "properties": {
103 | "@id": "node/256752379",
104 | "amenity": "pharmacy",
105 | "created_by": "JOSM",
106 | "dispensing": "yes",
107 | "name": "Longo"
108 | },
109 | "geometry": {
110 | "type": "Point",
111 | "coordinates": [
112 | 12.505337,
113 | 41.8950203
114 | ]
115 | }
116 | },
117 | {
118 | "type": "Feature",
119 | "id": "node/1166982158",
120 | "properties": {
121 | "@id": "node/1166982158",
122 | "amenity": "pharmacy",
123 | "name": "Farmacia Merulana Snc"
124 | },
125 | "geometry": {
126 | "type": "Point",
127 | "coordinates": [
128 | 12.5022586,
129 | 41.8910825
130 | ]
131 | }
132 | },
133 | {
134 | "type": "Feature",
135 | "id": "node/1166983851",
136 | "properties": {
137 | "@id": "node/1166983851",
138 | "amenity": "pharmacy",
139 | "name": "Farmacia Cuzzocrea Dottor Antonino"
140 | },
141 | "geometry": {
142 | "type": "Point",
143 | "coordinates": [
144 | 12.5033866,
145 | 41.8941826
146 | ]
147 | }
148 | },
149 | {
150 | "type": "Feature",
151 | "id": "node/1166984396",
152 | "properties": {
153 | "@id": "node/1166984396",
154 | "addr:city": "Roma",
155 | "addr:country": "IT",
156 | "addr:housenumber": "35",
157 | "addr:street": "Via dello Statuto",
158 | "amenity": "pharmacy",
159 | "dispensing": "yes",
160 | "name": "Farmacia Allo Statuto",
161 | "opening_hours": "24/7",
162 | "operator": "Dr. Pierluciano Pucci"
163 | },
164 | "geometry": {
165 | "type": "Point",
166 | "coordinates": [
167 | 12.5010875,
168 | 41.8950388
169 | ]
170 | }
171 | },
172 | {
173 | "type": "Feature",
174 | "id": "node/1324437899",
175 | "properties": {
176 | "@id": "node/1324437899",
177 | "addr:city": "Roma",
178 | "addr:country": "IT",
179 | "addr:housenumber": "49",
180 | "addr:street": "Piazza Barberini",
181 | "amenity": "pharmacy",
182 | "fax": "+39 06 48912560",
183 | "name": "Farmacia Internazionale",
184 | "opening_hours": "24/7",
185 | "operator": "Dottoressa Cervini Teresa",
186 | "phone": "+39 06 4825456"
187 | },
188 | "geometry": {
189 | "type": "Point",
190 | "coordinates": [
191 | 12.4884424,
192 | 41.9033713
193 | ]
194 | }
195 | },
196 | {
197 | "type": "Feature",
198 | "id": "node/1662148782",
199 | "properties": {
200 | "@id": "node/1662148782",
201 | "amenity": "pharmacy",
202 | "name": "Farmacia",
203 | "wheelchair": "limited"
204 | },
205 | "geometry": {
206 | "type": "Point",
207 | "coordinates": [
208 | 12.493189,
209 | 41.9008595
210 | ]
211 | }
212 | },
213 | {
214 | "type": "Feature",
215 | "id": "node/1979465981",
216 | "properties": {
217 | "@id": "node/1979465981",
218 | "addr:city": "Roma",
219 | "addr:country": "IT",
220 | "addr:housenumber": "29",
221 | "addr:postcode": "00185",
222 | "addr:street": "Via Marsala",
223 | "amenity": "pharmacy",
224 | "dispensing": "yes",
225 | "name": "Farmacrimi",
226 | "opening_hours": "Mo-Su,PH 07:30-22:00",
227 | "operator": "V. Crimi e Co.",
228 | "phone": "+39 06 4745421",
229 | "ref:vatin": "IT10491561006",
230 | "website": "http://www.farmacrimi.it"
231 | },
232 | "geometry": {
233 | "type": "Point",
234 | "coordinates": [
235 | 12.5031277,
236 | 41.9014832
237 | ]
238 | }
239 | },
240 | {
241 | "type": "Feature",
242 | "id": "node/2116655793",
243 | "properties": {
244 | "@id": "node/2116655793",
245 | "amenity": "pharmacy",
246 | "dispensing": "yes",
247 | "name": "Farmacia Viminale"
248 | },
249 | "geometry": {
250 | "type": "Point",
251 | "coordinates": [
252 | 12.494817,
253 | 41.8995944
254 | ]
255 | }
256 | },
257 | {
258 | "type": "Feature",
259 | "id": "node/3061568281",
260 | "properties": {
261 | "@id": "node/3061568281",
262 | "addr:city": "Roma",
263 | "addr:housenumber": "89",
264 | "addr:postcode": "00187",
265 | "addr:street": "Via della Stamperia",
266 | "amenity": "pharmacy",
267 | "name": "Farmacia Pesci"
268 | },
269 | "geometry": {
270 | "type": "Point",
271 | "coordinates": [
272 | 12.4836798,
273 | 41.9009134
274 | ]
275 | }
276 | }
277 | ]
278 | };
--------------------------------------------------------------------------------
/examples/data/restaurant.geojson.js:
--------------------------------------------------------------------------------
1 | var restaurant = {
2 | "type": "FeatureCollection",
3 | "generator": "overpass-turbo",
4 | "copyright": "The data included in this document is from www.openstreetmap.org. The data is made available under ODbL.",
5 | "timestamp": "2015-08-08T19:02:02Z",
6 | "features": [
7 | {
8 | "type": "Feature",
9 | "id": "node/252601050",
10 | "properties": {
11 | "@id": "node/252601050",
12 | "amenity": "restaurant",
13 | "cuisine": "chinese",
14 | "name": "Hang Zhou"
15 | },
16 | "geometry": {
17 | "type": "Point",
18 | "coordinates": [
19 | 12.4994694,
20 | 41.8957882
21 | ]
22 | }
23 | },
24 | {
25 | "type": "Feature",
26 | "id": "node/1479450328",
27 | "properties": {
28 | "@id": "node/1479450328",
29 | "amenity": "restaurant",
30 | "cuisine": "italian",
31 | "name": "Est Est Est !",
32 | "website": "www.anticapizzeriaricciroma.com"
33 | },
34 | "geometry": {
35 | "type": "Point",
36 | "coordinates": [
37 | 12.4926565,
38 | 41.8989567
39 | ]
40 | }
41 | },
42 | {
43 | "type": "Feature",
44 | "id": "node/1530298843",
45 | "properties": {
46 | "@id": "node/1530298843",
47 | "amenity": "restaurant",
48 | "name": "Hostaria Isidoro Al Colosseo"
49 | },
50 | "geometry": {
51 | "type": "Point",
52 | "coordinates": [
53 | 12.4986415,
54 | 41.8888149
55 | ]
56 | }
57 | },
58 | {
59 | "type": "Feature",
60 | "id": "node/1536870110",
61 | "properties": {
62 | "@id": "node/1536870110",
63 | "addr:housenumber": "40",
64 | "addr:street": "Largo Corrado Ricci",
65 | "amenity": "restaurant",
66 | "name": "Angelino ai Fori",
67 | "wheelchair": "limited"
68 | },
69 | "geometry": {
70 | "type": "Point",
71 | "coordinates": [
72 | 12.488007,
73 | 41.8930475
74 | ]
75 | }
76 | },
77 | {
78 | "type": "Feature",
79 | "id": "node/1536870121",
80 | "properties": {
81 | "@id": "node/1536870121",
82 | "addr:housenumber": "37",
83 | "amenity": "restaurant",
84 | "cuisine": "pizza",
85 | "name": "Pizzeria Imperiale",
86 | "wheelchair": "limited"
87 | },
88 | "geometry": {
89 | "type": "Point",
90 | "coordinates": [
91 | 12.4882146,
92 | 41.8931487
93 | ]
94 | }
95 | },
96 | {
97 | "type": "Feature",
98 | "id": "node/1542760517",
99 | "properties": {
100 | "@id": "node/1542760517",
101 | "amenity": "restaurant",
102 | "cuisine": "italian",
103 | "name": "Made in Sud"
104 | },
105 | "geometry": {
106 | "type": "Point",
107 | "coordinates": [
108 | 12.4956215,
109 | 41.8895889
110 | ]
111 | }
112 | },
113 | {
114 | "type": "Feature",
115 | "id": "node/1589901714",
116 | "properties": {
117 | "@id": "node/1589901714",
118 | "amenity": "restaurant",
119 | "name": "Kabir Fast Food"
120 | },
121 | "geometry": {
122 | "type": "Point",
123 | "coordinates": [
124 | 12.5046274,
125 | 41.8956069
126 | ]
127 | }
128 | },
129 | {
130 | "type": "Feature",
131 | "id": "node/1609106628",
132 | "properties": {
133 | "@id": "node/1609106628",
134 | "amenity": "restaurant",
135 | "cuisine": "italian",
136 | "name": "Pretoriana"
137 | },
138 | "geometry": {
139 | "type": "Point",
140 | "coordinates": [
141 | 12.5043739,
142 | 41.9050278
143 | ]
144 | }
145 | },
146 | {
147 | "type": "Feature",
148 | "id": "node/1660404815",
149 | "properties": {
150 | "@id": "node/1660404815",
151 | "amenity": "restaurant",
152 | "cuisine": "regional",
153 | "name": "Al Vino al Vino",
154 | "restaurant:type": "enoteca"
155 | },
156 | "geometry": {
157 | "type": "Point",
158 | "coordinates": [
159 | 12.4903691,
160 | 41.8955158
161 | ]
162 | }
163 | },
164 | {
165 | "type": "Feature",
166 | "id": "node/1660404843",
167 | "properties": {
168 | "@id": "node/1660404843",
169 | "amenity": "restaurant",
170 | "cuisine": "regional",
171 | "name": "Taverna Romana"
172 | },
173 | "geometry": {
174 | "type": "Point",
175 | "coordinates": [
176 | 12.4894514,
177 | 41.8940973
178 | ]
179 | }
180 | },
181 | {
182 | "type": "Feature",
183 | "id": "node/1660412593",
184 | "properties": {
185 | "@id": "node/1660412593",
186 | "addr:housenumber": "313",
187 | "amenity": "restaurant",
188 | "cuisine": "regional",
189 | "email": "cavour313@libero.it",
190 | "fax": "+39 06 6785496",
191 | "name": "Enoteca Cavour 313",
192 | "operator": "Enoteca Cavour 313 s.r.l.",
193 | "phone": "+39 06 6785496",
194 | "ref:vatin": "IT01238481004",
195 | "restaurant:type": "enoteca"
196 | },
197 | "geometry": {
198 | "type": "Point",
199 | "coordinates": [
200 | 12.489126,
201 | 41.8937195
202 | ]
203 | }
204 | },
205 | {
206 | "type": "Feature",
207 | "id": "node/1688847325",
208 | "properties": {
209 | "@id": "node/1688847325",
210 | "amenity": "restaurant",
211 | "cuisine": "italian",
212 | "name": "Gran Caffe Martini & Rossi"
213 | },
214 | "geometry": {
215 | "type": "Point",
216 | "coordinates": [
217 | 12.4941649,
218 | 41.8897129
219 | ]
220 | }
221 | },
222 | {
223 | "type": "Feature",
224 | "id": "node/1688847351",
225 | "properties": {
226 | "@id": "node/1688847351",
227 | "addr:housenumber": "5",
228 | "amenity": "restaurant",
229 | "cuisine": "italian",
230 | "name": "Al Gladiatore",
231 | "phone": "+39 06 77209496"
232 | },
233 | "geometry": {
234 | "type": "Point",
235 | "coordinates": [
236 | 12.494046,
237 | 41.8894459
238 | ]
239 | }
240 | },
241 | {
242 | "type": "Feature",
243 | "id": "node/1688847355",
244 | "properties": {
245 | "@id": "node/1688847355",
246 | "amenity": "restaurant",
247 | "cuisine": "italian",
248 | "name": "Royal art café"
249 | },
250 | "geometry": {
251 | "type": "Point",
252 | "coordinates": [
253 | 12.4942704,
254 | 41.8899551
255 | ]
256 | }
257 | },
258 | {
259 | "type": "Feature",
260 | "id": "node/1731734008",
261 | "properties": {
262 | "@id": "node/1731734008",
263 | "addr:city": "Roma",
264 | "addr:country": "IT",
265 | "addr:housenumber": "66",
266 | "addr:postcode": "00185",
267 | "addr:street": "Via Montebello",
268 | "amenity": "restaurant",
269 | "cuisine": "asian;pinoy;chinese",
270 | "name": "Asian Delight",
271 | "opening_hours": "Mo-Su 10:00-22:00; Sa 12:00-21:00",
272 | "operator": "F. Cojeda Contado SNC",
273 | "phone": "+39 06 48913216"
274 | },
275 | "geometry": {
276 | "type": "Point",
277 | "coordinates": [
278 | 12.5002433,
279 | 41.9055106
280 | ]
281 | }
282 | },
283 | {
284 | "type": "Feature",
285 | "id": "node/1923912448",
286 | "properties": {
287 | "@id": "node/1923912448",
288 | "addr:housenumber": "195",
289 | "amenity": "restaurant",
290 | "contact:phone": "+39 06 4824888",
291 | "drive_in": "no",
292 | "name": "L'archetto di Cavour",
293 | "wheelchair": "limited"
294 | },
295 | "geometry": {
296 | "type": "Point",
297 | "coordinates": [
298 | 12.4935056,
299 | 41.8951179
300 | ]
301 | }
302 | },
303 | {
304 | "type": "Feature",
305 | "id": "node/1926196447",
306 | "properties": {
307 | "@id": "node/1926196447",
308 | "amenity": "restaurant",
309 | "cuisine": "italian",
310 | "name": "Fantasy"
311 | },
312 | "geometry": {
313 | "type": "Point",
314 | "coordinates": [
315 | 12.49949,
316 | 41.9045268
317 | ]
318 | }
319 | },
320 | {
321 | "type": "Feature",
322 | "id": "node/1952358265",
323 | "properties": {
324 | "@id": "node/1952358265",
325 | "addr:city": "Roma",
326 | "addr:country": "IT",
327 | "addr:housenumber": "44/46",
328 | "addr:postcode": "00185",
329 | "addr:street": "Via Tiburtina",
330 | "amenity": "restaurant",
331 | "cuisine": "pizza",
332 | "name": "Pizzeria L'Economica",
333 | "operator": "Lori Giulio & C. s.a.s.",
334 | "oven": "wood_fired",
335 | "ref:vatin": "IT0390762210001",
336 | "restaurant:type:it": "pizzeria"
337 | },
338 | "geometry": {
339 | "type": "Point",
340 | "coordinates": [
341 | 12.5127833,
342 | 41.8974467
343 | ]
344 | }
345 | },
346 | {
347 | "type": "Feature",
348 | "id": "node/2612010515",
349 | "properties": {
350 | "@id": "node/2612010515",
351 | "addr:city": "Roma",
352 | "addr:housenumber": "21",
353 | "addr:postcode": "00187",
354 | "addr:street": "Via della Cordonata",
355 | "amenity": "restaurant",
356 | "cuisine": "regional",
357 | "name": "Santa Cristina al Quirinale",
358 | "phone": "+39 06 69925485",
359 | "website": "http://www.ristorantesantacristinaalquirinale.it"
360 | },
361 | "geometry": {
362 | "type": "Point",
363 | "coordinates": [
364 | 12.4864783,
365 | 41.8970834
366 | ]
367 | }
368 | },
369 | {
370 | "type": "Feature",
371 | "id": "node/2735002326",
372 | "properties": {
373 | "@id": "node/2735002326",
374 | "addr:housenumber": "315",
375 | "amenity": "restaurant",
376 | "name": "Baires Imperiale",
377 | "phone": "+39 06 69202164"
378 | },
379 | "geometry": {
380 | "type": "Point",
381 | "coordinates": [
382 | 12.4888985,
383 | 41.8936579
384 | ]
385 | }
386 | },
387 | {
388 | "type": "Feature",
389 | "id": "node/2735002356",
390 | "properties": {
391 | "@id": "node/2735002356",
392 | "addr:housenumber": "27",
393 | "amenity": "restaurant",
394 | "cuisine": "pizza",
395 | "name": "Tomoko Tudini",
396 | "phone": "+39 06 4817586; +39 06 4818487"
397 | },
398 | "geometry": {
399 | "type": "Point",
400 | "coordinates": [
401 | 12.4992448,
402 | 41.8994035
403 | ]
404 | }
405 | },
406 | {
407 | "type": "Feature",
408 | "id": "node/2925136606",
409 | "properties": {
410 | "@id": "node/2925136606",
411 | "addr:city": "Roma",
412 | "addr:housenumber": "56",
413 | "addr:postcode": "00187",
414 | "addr:street": "Vicolo dei Modelli",
415 | "amenity": "restaurant",
416 | "name": "La Fontana di Venere"
417 | },
418 | "geometry": {
419 | "type": "Point",
420 | "coordinates": [
421 | 12.4844365,
422 | 41.900612
423 | ]
424 | }
425 | },
426 | {
427 | "type": "Feature",
428 | "id": "node/2939415080",
429 | "properties": {
430 | "@id": "node/2939415080",
431 | "addr:city": "Roma",
432 | "addr:country": "IT",
433 | "addr:housename": "La Taverna Italiana",
434 | "addr:housenumber": "385",
435 | "addr:postcode": "00185",
436 | "addr:street": "Via Giovanni Giolitti",
437 | "amenity": "restaurant",
438 | "name": "La Taverna Italiana"
439 | },
440 | "geometry": {
441 | "type": "Point",
442 | "coordinates": [
443 | 12.5109193,
444 | 41.8940946
445 | ]
446 | }
447 | },
448 | {
449 | "type": "Feature",
450 | "id": "node/3052495271",
451 | "properties": {
452 | "@id": "node/3052495271",
453 | "addr:city": "Roma",
454 | "addr:housenumber": "24-24a",
455 | "addr:street": "Via dei Santi Quattro",
456 | "amenity": "restaurant",
457 | "cuisine": "pizza",
458 | "name": "Li Rioni"
459 | },
460 | "geometry": {
461 | "type": "Point",
462 | "coordinates": [
463 | 12.4978334,
464 | 41.8886879
465 | ]
466 | }
467 | },
468 | {
469 | "type": "Feature",
470 | "id": "node/3061568272",
471 | "properties": {
472 | "@id": "node/3061568272",
473 | "addr:city": "Roma",
474 | "addr:housenumber": "95",
475 | "addr:postcode": "00186",
476 | "addr:street": "Via in Arcione",
477 | "amenity": "restaurant",
478 | "name": "Al Presidente"
479 | },
480 | "geometry": {
481 | "type": "Point",
482 | "coordinates": [
483 | 12.4852166,
484 | 41.9015405
485 | ]
486 | }
487 | },
488 | {
489 | "type": "Feature",
490 | "id": "node/3061568276",
491 | "properties": {
492 | "@id": "node/3061568276",
493 | "addr:city": "Roma",
494 | "addr:housenumber": "41",
495 | "addr:postcode": "00186",
496 | "addr:street": "Via del Lavatore",
497 | "amenity": "restaurant",
498 | "name": "Birreria"
499 | },
500 | "geometry": {
501 | "type": "Point",
502 | "coordinates": [
503 | 12.4840258,
504 | 41.9008905
505 | ]
506 | }
507 | },
508 | {
509 | "type": "Feature",
510 | "id": "node/3061568277",
511 | "properties": {
512 | "@id": "node/3061568277",
513 | "addr:city": "Roma",
514 | "addr:housenumber": "34",
515 | "addr:street": "Via del Lavatore",
516 | "amenity": "restaurant",
517 | "name": "Da Cecere"
518 | },
519 | "geometry": {
520 | "type": "Point",
521 | "coordinates": [
522 | 12.4844496,
523 | 41.9011041
524 | ]
525 | }
526 | },
527 | {
528 | "type": "Feature",
529 | "id": "node/3101353149",
530 | "properties": {
531 | "@id": "node/3101353149",
532 | "addr:city": "Roma",
533 | "addr:housenumber": "9",
534 | "addr:street": "Via della Madonna dei Monti",
535 | "amenity": "restaurant",
536 | "cuisine": "italian",
537 | "name": "La Taverna dei Fori Imperiali"
538 | },
539 | "geometry": {
540 | "type": "Point",
541 | "coordinates": [
542 | 12.4883814,
543 | 41.8939729
544 | ]
545 | }
546 | },
547 | {
548 | "type": "Feature",
549 | "id": "node/3152274226",
550 | "properties": {
551 | "@id": "node/3152274226",
552 | "addr:city": "Roma",
553 | "addr:housenumber": "7",
554 | "addr:postcode": "00185",
555 | "addr:street": "Via Principe Amedeo",
556 | "amenity": "restaurant",
557 | "cuisine": "italian",
558 | "name": "Trattoria il Gallo Nero"
559 | },
560 | "geometry": {
561 | "type": "Point",
562 | "coordinates": [
563 | 12.4980494,
564 | 41.9000787
565 | ]
566 | }
567 | },
568 | {
569 | "type": "Feature",
570 | "id": "node/3190236550",
571 | "properties": {
572 | "@id": "node/3190236550",
573 | "addr:city": "Roma",
574 | "addr:housenumber": "91",
575 | "addr:street": "Via del Lavatore",
576 | "amenity": "restaurant",
577 | "cuisine": "pizza",
578 | "name": "Piccolo Buco",
579 | "phone": "+39 06 69380163"
580 | },
581 | "geometry": {
582 | "type": "Point",
583 | "coordinates": [
584 | 12.4846406,
585 | 41.9013199
586 | ]
587 | }
588 | },
589 | {
590 | "type": "Feature",
591 | "id": "node/3379569487",
592 | "properties": {
593 | "@id": "node/3379569487",
594 | "addr:city": "Roma",
595 | "addr:country": "IT",
596 | "addr:housenumber": "54",
597 | "addr:postcode": "00185",
598 | "addr:street": "Via Varese",
599 | "amenity": "restaurant",
600 | "cuisine": "italian",
601 | "email": "info@meidinnapols.it",
602 | "name": "Meid in Nepols",
603 | "oven": "wood_fired",
604 | "phone": "+39 06 44704131",
605 | "website": "http://www.meidinnepols.com/"
606 | },
607 | "geometry": {
608 | "type": "Point",
609 | "coordinates": [
610 | 12.5061437,
611 | 41.9023658
612 | ]
613 | }
614 | },
615 | {
616 | "type": "Feature",
617 | "id": "node/3548494475",
618 | "properties": {
619 | "@id": "node/3548494475",
620 | "addr:street": "San Martino dei Monte",
621 | "amenity": "restaurant",
622 | "cuisine": "italian",
623 | "name": "La Forchetta di Oro",
624 | "opening_hours": "mezze, serra",
625 | "smoking": "no",
626 | "wheelchair": "yes"
627 | },
628 | "geometry": {
629 | "type": "Point",
630 | "coordinates": [
631 | 12.4988453,
632 | 41.8956182
633 | ]
634 | }
635 | },
636 | {
637 | "type": "Feature",
638 | "id": "node/3554601726",
639 | "properties": {
640 | "@id": "node/3554601726",
641 | "addr:city": "Roma",
642 | "addr:country": "IT",
643 | "addr:housenumber": "107",
644 | "addr:postcode": "00184",
645 | "addr:street": "Via Cavour",
646 | "amenity": "restaurant",
647 | "cuisine": "italian",
648 | "name": "Ristorante-Pizzeria Gallo Matto",
649 | "outdoor_seating": "yes",
650 | "phone": "+39 06 470354"
651 | },
652 | "geometry": {
653 | "type": "Point",
654 | "coordinates": [
655 | 12.4954961,
656 | 41.8970481
657 | ]
658 | }
659 | },
660 | {
661 | "type": "Feature",
662 | "id": "node/3554607395",
663 | "properties": {
664 | "@id": "node/3554607395",
665 | "addr:city": "Roma",
666 | "addr:country": "IT",
667 | "addr:housenumber": "86",
668 | "addr:postcode": "00184",
669 | "addr:street": "Via Leonina",
670 | "amenity": "restaurant",
671 | "cuisine": "italian",
672 | "name": "Ristorante Ragno d'Oro",
673 | "outdoor_seating": "yes",
674 | "phone": "+39 06 4882003"
675 | },
676 | "geometry": {
677 | "type": "Point",
678 | "coordinates": [
679 | 12.492441,
680 | 41.8949731
681 | ]
682 | }
683 | },
684 | {
685 | "type": "Feature",
686 | "id": "node/3575003304",
687 | "properties": {
688 | "@id": "node/3575003304",
689 | "addr:city": "Roma",
690 | "addr:housenumber": "10",
691 | "addr:postcode": "00185",
692 | "addr:street": "Via del Castro Pretorio",
693 | "amenity": "restaurant",
694 | "cuisine": "japanese",
695 | "name": "Hokkaido",
696 | "opening_hours": "Mo-Su 11:00-15:00,18:00-24:00",
697 | "phone": "+39 06 45505297"
698 | },
699 | "geometry": {
700 | "type": "Point",
701 | "coordinates": [
702 | 12.5046543,
703 | 41.9012469
704 | ]
705 | }
706 | },
707 | {
708 | "type": "Feature",
709 | "id": "node/3658239261",
710 | "properties": {
711 | "@id": "node/3658239261",
712 | "addr:city": "Roma",
713 | "addr:housenumber": "58",
714 | "addr:street": "Piazza dei Santi Apostoli",
715 | "amenity": "restaurant",
716 | "cuisine": "italian",
717 | "name": "Bibo"
718 | },
719 | "geometry": {
720 | "type": "Point",
721 | "coordinates": [
722 | 12.4833635,
723 | 41.8974063
724 | ]
725 | }
726 | }
727 | ]
728 | };
--------------------------------------------------------------------------------
/examples/fuzzy.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Custom Filter Example: search markers with Fuzzy Search, using Fuse.js by Kirollos Risk
15 |
16 |
17 |
18 | Search restaurants in Rome, data by
OSM Overpass
19 | Example:
pizza,
vege,
japa,
giappo,
cucina romana,
chine ...
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
--------------------------------------------------------------------------------
/examples/geocoder.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 |
23 | Google GeoCoding API: search locations name by Google Maps API
24 |
25 |
26 |
27 |
28 |
29 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/examples/geocoding-google.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 |
23 | Google GeoCoding API: search locations name by Google Maps API
24 |
25 |
26 |
27 |
Search values:
28 | Google Geocoding API
29 |
developers.google.com
30 |
31 |
32 |
33 |
34 |
35 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
--------------------------------------------------------------------------------
/examples/geocoding-nominatim.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
19 |
20 |
21 |
22 |
23 |
24 | GeoCode Search Example: search locations name by Nominatim Openstreetmap Service
25 |
26 |
27 |
28 |
Search values:
29 | OpenStreetMap Data offer by
30 |
nominatim.osm.org
31 |
32 |
33 |
34 |
35 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
--------------------------------------------------------------------------------
/examples/geojson-layer.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | GeoJSON Example: search vector features in GeoJSON layer by property
15 |
16 |
17 |
18 | Search US states name:
19 | Alabama, Arizona, Colorado, Maryland, Michigan, North Carolina, Pennsylvania, Wyoming ...
20 |
21 |
22 |
23 |
24 |
25 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
--------------------------------------------------------------------------------
/examples/jsonp-filtered.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
23 |
24 |
25 |
26 |
27 |
28 | JSONP Example: search locations by third party jsonp service, with filter data
29 |
30 |
31 |
32 |
Search values:
33 | OpenStreetMap Data offer by MapQuest Open Platform
34 |
open.mapquestapi.com
35 |
36 |
37 | native JSON
38 |
39 |
40 |
41 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/examples/jsonp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | JSONP Example: search locations by jsonp service
15 |
16 |
17 |
18 | Search values:
19 | aquamarine, black, blue, cyan, darkblue, darkred, darkgray, dodgerblue, gray, green, red, skyblue, yellow, white ...
20 |
21 |
22 |
23 |
24 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/examples/location-url.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
18 |
19 |
20 |
21 |
22 |
23 | Location on remote page: search locations name and open it in osm website
24 |
25 |
26 |
27 |
Search values:
28 | OpenStreetMap Data offer by
29 |
nominatim.osm.org
30 |
31 |
32 |
33 |
34 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
--------------------------------------------------------------------------------
/examples/methods.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 | Methods Example: search text by method
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 | Search values:
24 | aquamarine, black, blue, cyan, darkblue, darkred, darkgray, dodgerblue, gray, green, red, skyblue, yellow, white ...
25 |
26 |
27 |
28 |
29 |
30 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/examples/mobile.css:
--------------------------------------------------------------------------------
1 | html,body {
2 | margin:0;
3 | padding:0;
4 | }
5 |
6 | h3 {
7 | font-size:.85em;
8 | }
9 | h3,h4 {
10 | white-space:nowrap;
11 | float:left;
12 | margin:2px;
13 | padding:0;
14 | }
15 | #copy {
16 | right:-4px;
17 | }
18 | #map {
19 | position:absolute;
20 | top:22px;
21 | left:0;
22 | right:0;
23 | bottom:0;
24 | border:none;
25 | border-top:1px solid #1978cf;
26 | height:auto;
27 | width:auto;
28 | }
29 | #post-it {
30 | position:absolute;
31 | right:5px;
32 | top:40px;
33 | height:90px;
34 | width:90px;
35 | font-size:.65em;
36 | }
37 |
38 | /********** Leaflet Customizations for **********/
39 |
40 | /* CONTROLS */
41 | .leaflet-bottom .leaflet-control,
42 | .leaflet-left .leaflet-control {
43 | box-shadow: 0 0 8px rgba(0,0,0,0.4);
44 | border: 2px solid #1978cf;
45 | border-radius:6px;
46 | }
47 | .leaflet-left.leaflet-top .leaflet-control,
48 | .leaflet-touch .leaflet-left .leaflet-control {
49 | margin-left: 8px;
50 | margin-top: 8px;
51 | margin-bottom: 0;
52 | }
53 | .leaflet-left.leaflet-bottom .leaflet-control {
54 | margin-left: 8px;
55 | margin-bottom: 8px;
56 | margin-top: 0;
57 | }
58 |
59 | /* ZOOM */
60 | .leaflet-control-zoom,
61 | .leaflet-touch .leaflet-control-zoom {
62 | border-radius:4px;
63 | }
64 | .leaflet-control-zoom a,
65 | .leaflet-touch .leaflet-control-zoom a,
66 | .leaflet-bar a,
67 | .leaflet-touch .leaflet-bar a {
68 | background-color: rgba(255, 255, 255, 0.8);
69 | width: 32px;
70 | height: 28px;
71 | }
72 | .leaflet-touch .leaflet-control-zoom-out {
73 | line-height: 24px;
74 | }
75 | .leaflet-control-zoom a:hover,
76 | .leaflet-touch .leaflet-control-zoom a:hover,
77 | .leaflet-bar a:hover,
78 | .leaflet-touch .leaflet-bar a:hover {
79 | background-color: #fff;
80 | width: 32px;
81 | height: 28px;
82 | }
83 |
84 | .leaflet-touch .leaflet-bar a:first-child {
85 | -webkit-border-top-left-radius: 4px;
86 | border-top-left-radius: 4px;
87 | -webkit-border-top-right-radius: 4px;
88 | border-top-right-radius: 4px;
89 | }
90 | .leaflet-touch .leaflet-bar a:last-child {
91 | -webkit-border-bottom-left-radius: 4px;
92 | border-bottom-left-radius: 4px;
93 | -webkit-border-bottom-right-radius: 4px;
94 | border-bottom-right-radius: 4px;
95 | border-bottom: none;
96 | }
97 |
98 | /* ATTRIBUTION*/
99 | .leaflet-control.leaflet-control-attribution {
100 | border:none;
101 | padding:0 4px 2px 4px;
102 | margin:0 -4px -4px 0;
103 | }
104 |
105 |
--------------------------------------------------------------------------------
/examples/mobile.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet Search Mobile Example
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
Search values:
21 | OpenStreetMap Data offer by MapQuest Open Platform
22 |
open.mapquestapi.com
23 |
24 |
25 |
26 |
27 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/examples/multiple-layers.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
46 |
47 |
48 |
49 |
50 |
51 | Multiple Layers Example: search markers in multiple layers
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
--------------------------------------------------------------------------------
/examples/multiple-results.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Multiple search Example: search multiple markers have same title
15 |
16 |
17 |
18 | Search values:
19 | Darkgray, dodgerblue, gray, green, seashell
20 |
21 |
22 |
23 |
24 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
--------------------------------------------------------------------------------
/examples/nominatim.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
--------------------------------------------------------------------------------
/examples/outside.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
29 |
30 |
31 |
32 |
33 |
34 | Outside Div Example: move Seach Control outside the map div
35 |
36 |
37 |
38 |
39 | Search values:
40 | aquamarine, black, blue, cyan, darkblue, darkred, darkgray, dodgerblue, gray, green, red, skyblue, yellow, white ...
41 |
42 |
43 |
44 |
45 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
--------------------------------------------------------------------------------
/examples/popup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Multiple Layers Example: search markers in multiple layers
15 |
16 |
17 |
18 |
19 |
20 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/examples/requirejs.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | RequireJs Example: load library by AMD module compatible
17 |
18 |
19 |
20 | Search values:
21 | aquamarine, black, blue, cyan, darkblue, darkred, darkgray, dodgerblue, gray, green, red, skyblue, yellow, white ...
22 |
23 |
24 |
25 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/examples/search.php:
--------------------------------------------------------------------------------
1 | 0, 'errmsg'=>'specify q parameter') ) );
53 |
54 | $data = json_decode('[
55 | {"loc":[41.575330,13.102411], "title":"aquamarine"},
56 | {"loc":[41.575730,13.002411], "title":"black"},
57 | {"loc":[41.807149,13.162994], "title":"blue"},
58 | {"loc":[41.507149,13.172994], "title":"chocolate"},
59 | {"loc":[41.847149,14.132994], "title":"coral"},
60 | {"loc":[41.219190,13.062145], "title":"cyan"},
61 | {"loc":[41.344190,13.242145], "title":"darkblue"},
62 | {"loc":[41.679190,13.122145], "title":"darkred"},
63 | {"loc":[41.329190,13.192145], "title":"darkgray"},
64 | {"loc":[41.379290,13.122545], "title":"dodgerblue"},
65 | {"loc":[41.409190,13.362145], "title":"gray"},
66 | {"loc":[41.794008,12.583884], "title":"green"},
67 | {"loc":[41.805008,12.982884], "title":"greenyellow"},
68 | {"loc":[41.536175,13.273590], "title":"red"},
69 | {"loc":[41.516175,13.373590], "title":"rosybrown"},
70 | {"loc":[41.506175,13.173590], "title":"royalblue"},
71 | {"loc":[41.836175,13.673590], "title":"salmon"},
72 | {"loc":[41.796175,13.570590], "title":"seagreen"},
73 | {"loc":[41.436175,13.573590], "title":"seashell"},
74 | {"loc":[41.336175,13.973590], "title":"silver"},
75 | {"loc":[41.236175,13.273590], "title":"skyblue"},
76 | {"loc":[41.546175,13.473590], "title":"yellow"},
77 | {"loc":[41.239190,13.032145], "title":"white"}
78 | ]',true); //SIMULATE A DATABASE data
79 | //the searched field is: title
80 |
81 | if(isset($_GET['cities'])) //SIMULATE A BIG DATABASE, for ajax-bulk.html example
82 | $data = json_decode( file_get_contents('cities15000.json'), true);
83 | //load big data store, cities15000.json (about 14000 records)
84 |
85 | function searchInit($text) //search initial text in titles
86 | {
87 | $reg = "/^".$_GET['q']."/i"; //initial case insensitive searching
88 | return (bool)@preg_match($reg, $text['title']);
89 | }
90 | $fdata = array_filter($data, 'searchInit'); //filter data
91 | $fdata = array_values($fdata); //reset $fdata indexs
92 |
93 | $JSON = json_encode($fdata,true);
94 |
95 | #if($_SERVER['REMOTE_ADDR']=='127.0.0.1') sleep(1);
96 | //simulate connection latency for localhost tests
97 | @header("Content-type: application/json; charset=utf-8");
98 |
99 | if(isset($_GET['callback']) and !empty($_GET['callback'])) //support for JSONP request
100 | echo $_GET['callback']."($JSON)";
101 | else
102 | echo $JSON; //AJAX request
103 |
104 |
105 | ?>
106 |
--------------------------------------------------------------------------------
/examples/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 | Simple Example: search markers in layer by title
15 |
16 |
17 |
18 | Search values:
19 | aquamarine, black, blue, cyan, darkblue, darkred, darkgray, dodgerblue, gray, green, red, skyblue, yellow, white ...
20 |
21 |
22 |
23 |
24 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
--------------------------------------------------------------------------------
/examples/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background:#b5d0d0;
3 | color:#285585;
4 | font-family:Arial;
5 | }
6 | body#home {
7 | background:url('../images/back.png') no-repeat top left #b5d0d0;
8 | margin-left:150px;
9 | }
10 |
11 | a {
12 | color:#1978cf;
13 | }
14 | a:hover {
15 | color:#fff;
16 | }
17 | h2, h3, h4 {
18 | white-space:nowrap;
19 | margin:1em 0 0 0;
20 | }
21 | h3 a,
22 | h3 a:hover {
23 | text-decoration:none;
24 | }
25 | #desc {
26 | float: left;
27 | margin-bottom: 1em;
28 | position: relative;
29 | white-space:nowrap;
30 | font-size:1em;
31 | }
32 | #map {
33 | border-radius:.125em;
34 | border:2px solid #1978cf;
35 | margin: 4px 0;
36 | float:left;
37 | width:600px;
38 | height:400px;
39 | }
40 | ul {
41 | font-size:.85em;
42 | margin:0;
43 | padding:0;
44 | }
45 | li {
46 | margin:0 0 2px 18px;
47 | }
48 | #post-it {
49 | width:9em;
50 | height:9em;
51 | margin-left:2em;
52 | padding:1em;
53 | float:left;
54 | background:#fbf5bf;
55 | border:1px solid #c6bb58;
56 | box-shadow: 2px 2px 6px #999;
57 | color:#666;
58 | }
59 | #copy {
60 | position:fixed;
61 | z-index:1000;
62 | right:150px;
63 | top:-8px;
64 | font-size:.85em;
65 | padding:8px 8px 2px 8px;
66 | background: #323b44;
67 | border: 2px solid #737c85;
68 | border-radius:.7em;
69 | opacity: 0.9;
70 | box-shadow:0 0 8px #5f7182;
71 | color:#eee
72 | }
73 | #copy a {
74 | color:#ccc;
75 | text-decoration:none
76 | }
77 | #copy a:hover {
78 | color:#fff
79 | }
80 | #ribbon {
81 | position: absolute;
82 | top: 0;
83 | right: 0;
84 | border: 0;
85 | filter: alpha(opacity=80);
86 | -khtml-opacity: .8;
87 | -moz-opacity: .8;
88 | opacity: .8;
89 | }
90 | .contents {
91 | float:left;
92 | margin:0 2em 2em 0;
93 | }
94 | #comments {
95 | clear:both;
96 | }
97 |
98 |
--------------------------------------------------------------------------------
/examples/tests.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
25 |
26 |
27 |
28 |
29 | Testing area
30 |
31 |
32 |
33 |
34 |
35 |
36 |
search in layer
37 |
38 |
84 |
85 |
86 |
87 |
search in GeoJSON
88 |
89 |
90 |
118 |
119 |
120 |
121 |
Ajax
122 |
123 |
133 |
134 |
135 |
136 |
JSONP
137 |
138 |
148 |
149 |
150 |
151 |
sourceData AJAX(by jQuery)
152 |
153 |
173 |
174 |
175 |
176 |
jsonpUrl, formatData, Cities
177 |
178 |
219 |
220 |
221 |
222 |
223 |
search in layer, Custom Tip
224 |
225 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
--------------------------------------------------------------------------------
/images/back.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/images/back.png
--------------------------------------------------------------------------------
/images/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/images/favicon.png
--------------------------------------------------------------------------------
/images/leaflet-search.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/images/leaflet-search.jpg
--------------------------------------------------------------------------------
/images/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/images/loader.gif
--------------------------------------------------------------------------------
/images/search-icon-mobile.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/images/search-icon-mobile.png
--------------------------------------------------------------------------------
/images/search-icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-search/d29c5392b33cc31b338c3d17b5f8532fef5eae48/images/search-icon.png
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet.Control.Search
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Leaflet.Control.Search
14 |
15 |
16 | A Leaflet Control for search markers/features location by attribute
17 | and much more.
18 |
19 |
20 |
21 |
22 | Other useful stuff for
Web Mapping...
23 |
24 |
25 |
26 | If any of these open source solutions help your work and saved you time consider sending a donation
27 |
35 |
36 |
37 |
38 |
39 |
Features
40 |
41 | - Autocomplete
42 | - No require external Ajax libs
43 | - Retrieve data locations by Ajax/Jsonp
44 | - Pre-filtering data from Ajax/Jsonp
45 | - Complete fields remapping for remote Jsonp service
46 | - Data source callback support
47 | - Localization placeholder and text alert
48 | - Autozoom on location founded
49 | - Autoresize textbox
50 | - Customize tooltip menu
51 | - Many options to customize the behavior
52 | - Support search in features collection
53 | - Render Search Box Outside the Leaflet Map
54 | - AMD and CommonJS compatible
55 |
56 |
57 |
80 |
81 |
Code repositories
82 |
Github.com
83 |
84 |
Node Packaged Module
85 |
86 |
Website
87 |
opengeo.tech/maps/leaflet-search
88 |
89 |
90 |
Download
91 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
110 |
111 |
114 |
115 |
116 |
117 |
118 |
119 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2023 Stefano Cudini
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leaflet-search",
3 | "version": "4.0.0",
4 | "description": "Leaflet Control for searching markers/features by attribute on map or remote searching in jsonp/ajax",
5 | "repository": {
6 | "type": "git",
7 | "url": "git@github.com:stefanocudini/leaflet-search.git"
8 | },
9 | "homepage": "https://opengeo.tech/maps/leaflet-search/",
10 | "author": {
11 | "name": "Stefano Cudini",
12 | "email": "stefano.cudini@gmail.com",
13 | "url": "https://opengeo.tech/"
14 | },
15 | "main": "dist/leaflet-search.src.js",
16 | "style": "dist/leaflet-search.src.css",
17 | "license": "MIT",
18 | "keywords": [
19 | "gis",
20 | "map",
21 | "leaflet"
22 | ],
23 | "scripts": {},
24 | "dependencies": {
25 | "leaflet": "*"
26 | },
27 | "devDependencies": {
28 | "grunt": "^1.4.1",
29 | "grunt-banner": "^0.6.0",
30 | "grunt-cli": "^1.4.3",
31 | "grunt-contrib-clean": "^2.0.0",
32 | "grunt-contrib-concat": "^2.0.0",
33 | "grunt-contrib-cssmin": "^4.0.0",
34 | "grunt-contrib-jshint": "^3.2.0",
35 | "grunt-contrib-uglify": "^5.0.1",
36 | "grunt-contrib-watch": "^1.1.0",
37 | "grunt-remove-logging": "^0.2.0",
38 | "standard": "^17.0.0"
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/src/leaflet-search-geocoder.js:
--------------------------------------------------------------------------------
1 | /*
2 | this file is work in progress and represent an extension
3 | of the main plugin to support programmatically most famous geocoder services
4 |
5 | the base idea is:
6 | - any geocoder services is identified by name passed to the plugin option
7 | - any geocoder sub module implemnt custom parameters anc a custom callback to extract resulta in leaflet search format result
8 | - any geocoder accept only two parameters, api key and user key, passed to remote service
9 |
10 | any contributions is welcome <3
11 |
12 | */
13 | (function (factory) {
14 | // eslint-disable-next-line
15 | if (typeof define === 'function' && define.amd) {
16 | // AMD
17 | // eslint-disable-next-line
18 | define(['leaflet'], factory)
19 | } else if (typeof module !== 'undefined') {
20 | // Node/CommonJS
21 | module.exports = factory(require('leaflet'))
22 | } else {
23 | // Browser globals
24 | if (typeof window.L === 'undefined') { throw new Error('Leaflet must be loaded first') }
25 | factory(window.L)
26 | }
27 | })(function (L) {
28 | L.Control.Search.include({
29 | options: {
30 | geocoder: 'google',
31 | markerLocation: true,
32 | autoType: false,
33 | autoCollapse: true,
34 | minLength: 2
35 | },
36 | /* onAdd: function (map) {
37 | L.Control.Search.prototype.onAdd.call(this, map);
38 | console.log('Geocoder',this.options)
39 | }, */
40 | geocoders: {
41 | /*
42 | 'google': {
43 | urlTmpl: "//maps.googleapis.com/maps/api/geocode/json?key={key}&address={text}"
44 | //todo others
45 | },
46 | 'here': {
47 | urlTmpl: https://geocoder.ls.hereapi.com/6.2/geocode.json?apiKey={apiKey}&searchtext={text}"
48 | params: function(opts, text) {
49 |
50 | //opts is leaflet options input
51 | //text is input text searched
52 |
53 | return {
54 | 'apiKey': opts.apikey,
55 | 'format': 'json',
56 | 'q': text,
57 | 'jsonp': 'herejsoncallback',
58 | };
59 | },
60 | callback: function(resp) {
61 | //TODO refact resp data
62 | }
63 |
64 | "//nominatim.openstreetmap.org/search?"
65 | } */
66 | }
67 | })
68 | })
69 |
--------------------------------------------------------------------------------
/src/leaflet-search.css:
--------------------------------------------------------------------------------
1 |
2 | .leaflet-container .leaflet-control-search {
3 | position:relative;
4 | float:left;
5 | background:#fff;
6 | color:#1978cf;
7 | border: 2px solid rgba(0,0,0,0.2);
8 | background-clip: padding-box;
9 | -moz-border-radius: 4px;
10 | -webkit-border-radius: 4px;
11 | border-radius: 4px;
12 | background-color: rgba(255, 255, 255, 0.8);
13 | z-index:1000;
14 | margin-left: 10px;
15 | margin-top: 10px;
16 | }
17 | .leaflet-control-search.search-exp {/*expanded*/
18 | background: #fff;
19 | border: 2px solid rgba(0,0,0,0.2);
20 | background-clip: padding-box;
21 | }
22 | .leaflet-control-search .search-input {
23 | display:block;
24 | float:left;
25 | background: #fff;
26 | border:1px solid #666;
27 | border-radius:2px;
28 | height:22px;
29 | padding:0 20px 0 2px;
30 | margin:4px 0 4px 4px;
31 | }
32 | .leaflet-control-search.search-load .search-input {
33 | background: url('../images/loader.gif') no-repeat center right #fff;
34 | }
35 | .leaflet-control-search.search-load .search-cancel {
36 | visibility:hidden;
37 | }
38 | .leaflet-control-search .search-cancel {
39 | display:block;
40 | width:22px;
41 | height:22px;
42 | position:absolute;
43 | right:28px;
44 | margin:6px 0;
45 | background: url('../images/search-icon.png') no-repeat 0 -46px;
46 | text-decoration:none;
47 | filter: alpha(opacity=80);
48 | opacity: 0.8;
49 | }
50 | .leaflet-control-search .search-cancel:hover {
51 | filter: alpha(opacity=100);
52 | opacity: 1;
53 | }
54 | .leaflet-control-search .search-cancel span {
55 | display:none;/* comment for cancel button imageless */
56 | font-size:18px;
57 | line-height:20px;
58 | color:#ccc;
59 | font-weight:bold;
60 | }
61 | .leaflet-control-search .search-cancel:hover span {
62 | color:#aaa;
63 | }
64 | .leaflet-control-search .search-button {
65 | display:block;
66 | float:left;
67 | width:30px;
68 | height:30px;
69 | background: url('../images/search-icon.png') no-repeat 4px 4px #fff;
70 | border-radius:4px;
71 | }
72 | .leaflet-control-search .search-button:hover {
73 | background: url('../images/search-icon.png') no-repeat 4px -20px #fafafa;
74 | }
75 | .leaflet-control-search .search-tooltip {
76 | position:absolute;
77 | top:100%;
78 | left:0;
79 | float:left;
80 | list-style: none;
81 | padding-left: 0;
82 | min-width:120px;
83 | max-height:122px;
84 | box-shadow: 1px 1px 6px rgba(0,0,0,0.4);
85 | background-color: rgba(0, 0, 0, 0.25);
86 | z-index:1010;
87 | overflow-y:auto;
88 | overflow-x:hidden;
89 | cursor: pointer;
90 | }
91 | .leaflet-control-search .search-tip {
92 | margin:2px;
93 | padding:2px 4px;
94 | display:block;
95 | color:black;
96 | background: #eee;
97 | border-radius:.25em;
98 | text-decoration:none;
99 | white-space:nowrap;
100 | vertical-align:middle;
101 | }
102 | .leaflet-control-search .search-button:hover {
103 | background-color: #f4f4f4;
104 | }
105 | .leaflet-control-search .search-tip-select,
106 | .leaflet-control-search .search-tip:hover {
107 | background-color: #fff;
108 | }
109 | .leaflet-control-search .search-alert {
110 | cursor:pointer;
111 | clear:both;
112 | font-size:.75em;
113 | margin-bottom:5px;
114 | padding:0 .25em;
115 | color:#e00;
116 | font-weight:bold;
117 | border-radius:.25em;
118 | }
119 |
120 |
121 |
--------------------------------------------------------------------------------
/src/leaflet-search.js:
--------------------------------------------------------------------------------
1 | /*
2 | Name Data passed Description
3 |
4 | Managed Events:
5 | search:locationfound {latlng, title, layer} fired after moved and show markerLocation
6 | search:expanded {} fired after control was expanded
7 | search:collapsed {} fired after control was collapsed
8 | search:cancel {} fired after cancel button clicked
9 |
10 | Public methods:
11 | setLayer() L.LayerGroup() set layer search at runtime
12 | showAlert() 'Text message' show alert message
13 | searchText() 'Text searched' search text by external code
14 | */
15 |
16 | // TODO implement can do research on multiple sources layers and remote
17 | // TODO history: false, //show latest searches in tooltip
18 | // FIXME option condition problem {autoCollapse: true, markerLocation: true} not show location
19 | // FIXME option condition problem {autoCollapse: false }
20 | //
21 | // TODO here insert function search inputText FIRST in _recordsCache keys and if not find results..
22 | // run one of callbacks search(sourceData,jsonpUrl or options.layer) and run this.showTooltip
23 | //
24 | // TODO change structure of _recordsCache
25 | // like this: _recordsCache = {"text-key1": {loc:[lat,lng], ..other attributes.. }, {"text-key2": {loc:[lat,lng]}...}, ...}
26 | // in this mode every record can have a free structure of attributes, only 'loc' is required
27 | // TODO important optimization!!! always append data in this._recordsCache
28 | // now _recordsCache content is emptied and replaced with new data founded
29 | // always appending data on _recordsCache give the possibility of caching ajax, jsonp and layersearch!
30 | //
31 | // TODO here insert function search inputText FIRST in _recordsCache keys and if not find results..
32 | // run one of callbacks search(sourceData,jsonpUrl or options.layer) and run this.showTooltip
33 | //
34 | // TODO change structure of _recordsCache
35 | // like this: _recordsCache = {"text-key1": {loc:[lat,lng], ..other attributes.. }, {"text-key2": {loc:[lat,lng]}...}, ...}
36 | // in this way every record can have a free structure of attributes, only 'loc' is required
37 |
38 | (function (factory) {
39 | // eslint-disable-next-line
40 | if (typeof define === 'function' && define.amd) {
41 | // AMD
42 | // eslint-disable-next-line
43 | define(['leaflet'], factory)
44 | } else if (typeof module !== 'undefined') {
45 | // Node/CommonJS
46 | module.exports = factory(require('leaflet'))
47 | } else {
48 | // Browser globals
49 | if (typeof window.L === 'undefined') { throw new Error('Leaflet must be loaded first') }
50 | factory(window.L)
51 | }
52 | })(function (L) {
53 | L.Control.Search = L.Control.extend({
54 |
55 | includes: L.version[0] === '1' ? L.Evented.prototype : L.Mixin.Events,
56 |
57 | options: {
58 | url: '', // url for search by ajax request, ex: "search.php?q={s}". Can be function to returns string for dynamic parameter setting
59 | layer: null, // layer where search markers(is a L.LayerGroup)
60 | sourceData: null, // function to fill _recordsCache, passed searching text by first param and callback in second
61 | // TODO implements uniq option 'sourceData' to recognizes source type: url,array,callback or layer
62 | jsonpParam: null, // jsonp param name for search by jsonp service, ex: "callback"
63 | propertyLoc: 'loc', // field for remapping location, using array: ['latname','lonname'] for select double fields(ex. ['lat','lon'] ) support dotted format: 'prop.subprop.title'
64 | propertyName: 'title', // property in marker.options(or feature.properties for vector layer) trough filter elements in layer,
65 | formatData: null, // callback for reformat all data from source to indexed data object
66 | filterData: null, // callback for filtering data from text searched, params: textSearch, allRecords
67 | moveToLocation: null, // callback run on location found, params: latlng, title, map
68 | buildTip: null, // function to return row tip html node(or html string), receive text tooltip in first param
69 | container: '', // container id to insert Search Control
70 | zoom: null, // default zoom level for move to location
71 | minLength: 1, // minimal text length for autocomplete
72 | initial: true, // search elements only by initial text
73 | casesensitive: false, // search elements in case sensitive text
74 | autoType: true, // complete input with first suggested result and select this filled-in text.
75 | delayType: 400, // delay while typing for show tooltip
76 | tooltipLimit: -1, // limit max results to show in tooltip. -1 for no limit, 0 for no results
77 | tipAutoSubmit: true, // auto map panTo when click on tooltip
78 | firstTipSubmit: false, // auto select first result con enter click
79 | autoResize: true, // autoresize on input change
80 | collapsed: true, // collapse search control at startup
81 | autoCollapse: false, // collapse search control after submit(on button or on tips if enabled tipAutoSubmit)
82 | autoCollapseTime: 1200, // delay for autoclosing alert and collapse after blur
83 | textErr: 'Location not found', // error message
84 | textCancel: 'Cancel', // title in cancel button
85 | textPlaceholder: 'Search...', // placeholder value
86 | hideMarkerOnCollapse: false, // remove circle and marker on search control collapsed
87 | position: 'topleft',
88 | marker: { // custom L.Marker or false for hide
89 | icon: false, // custom L.Icon for maker location or false for hide
90 | animate: true, // animate a circle over location found
91 | circle: { // draw a circle in location found
92 | radius: 10,
93 | weight: 3,
94 | color: '#e03',
95 | stroke: true,
96 | fill: false
97 | }
98 | }
99 | },
100 |
101 | _getPath: function (obj, prop) {
102 | const parts = prop.split('.')
103 | const last = parts.pop()
104 | const len = parts.length
105 | let cur = parts[0]
106 | let i = 1
107 |
108 | if (len > 0) {
109 | while ((obj = obj[cur]) && i < len) { cur = parts[i++] }
110 | }
111 |
112 | if (obj) { return obj[last] }
113 | },
114 |
115 | _isObject: function (obj) {
116 | return Object.prototype.toString.call(obj) === '[object Object]'
117 | },
118 |
119 | initialize: function (options) {
120 | L.Util.setOptions(this, options || {})
121 | this._inputMinSize = this.options.textPlaceholder ? this.options.textPlaceholder.length : 10
122 | this._layer = this.options.layer || new L.LayerGroup()
123 | this._filterData = this.options.filterData || this._defaultFilterData
124 | this._formatData = this.options.formatData || this._defaultFormatData
125 | this._moveToLocation = this.options.moveToLocation || this._defaultMoveToLocation
126 | this._autoTypeTmp = this.options.autoType // useful for disable autoType temporarily in delete/backspace keydown
127 | this._countertips = 0 // number of tips items
128 | this._recordsCache = {} // key,value table! to store locations! format: key,latlng
129 | this._curReq = null
130 | },
131 |
132 | onAdd: function (map) {
133 | this._map = map
134 | this._container = L.DomUtil.create('div', 'leaflet-control-search')
135 | this._input = this._createInput(this.options.textPlaceholder, 'search-input')
136 | this._tooltip = this._createTooltip('search-tooltip')
137 | this._cancel = this._createCancel(this.options.textCancel, 'search-cancel')
138 | this._button = this._createButton(this.options.textPlaceholder, 'search-button')
139 | this._alert = this._createAlert('search-alert')
140 |
141 | if (this.options.collapsed === false) { this.expand(this.options.collapsed) }
142 |
143 | if (this.options.marker) {
144 | if (this.options.marker instanceof L.Marker || this.options.marker instanceof L.CircleMarker) { this._markerSearch = this.options.marker } else if (this._isObject(this.options.marker)) { this._markerSearch = new L.Control.Search.Marker([0, 0], this.options.marker) }
145 |
146 | this._markerSearch._isMarkerSearch = true
147 | }
148 |
149 | this.setLayer(this._layer)
150 |
151 | map.on({
152 | // 'layeradd': this._onLayerAddRemove,
153 | // 'layerremove': this._onLayerAddRemove
154 | resize: this._handleAutoresize
155 | }, this)
156 | return this._container
157 | },
158 | addTo: function (map) {
159 | if (this.options.container) {
160 | this._container = this.onAdd(map)
161 | this._wrapper = L.DomUtil.get(this.options.container)
162 | this._wrapper.style.position = 'relative'
163 | this._wrapper.appendChild(this._container)
164 | } else { L.Control.prototype.addTo.call(this, map) }
165 |
166 | return this
167 | },
168 |
169 | onRemove: function (map) {
170 | this._recordsCache = {}
171 | // map.off({
172 | // 'layeradd': this._onLayerAddRemove,
173 | // 'layerremove': this._onLayerAddRemove
174 | // }, this);
175 | map.off({
176 | // 'layeradd': this._onLayerAddRemove,
177 | // 'layerremove': this._onLayerAddRemove
178 | resize: this._handleAutoresize
179 | }, this)
180 | },
181 |
182 | // _onLayerAddRemove: function(e) {
183 | // //without this, run setLayer also for each Markers!! to optimize!
184 | // if(e.layer instanceof L.LayerGroup)
185 | // if( L.stamp(e.layer) != L.stamp(this._layer) )
186 | // this.setLayer(e.layer);
187 | // },
188 |
189 | setLayer: function (layer) { // set search layer at runtime
190 | // this.options.layer = layer; //setting this, run only this._recordsFromLayer()
191 | this._layer = layer
192 | this._layer.addTo(this._map)
193 | return this
194 | },
195 |
196 | showAlert: function (text) {
197 | const self = this
198 | text = text || this.options.textErr
199 | this._alert.style.display = 'block'
200 | this._alert.innerHTML = text
201 | clearTimeout(this.timerAlert)
202 |
203 | this.timerAlert = setTimeout(function () {
204 | self.hideAlert()
205 | }, this.options.autoCollapseTime)
206 | return this
207 | },
208 |
209 | hideAlert: function () {
210 | this._alert.style.display = 'none'
211 | return this
212 | },
213 |
214 | cancel: function () {
215 | this._input.value = ''
216 | this._handleKeypress({ keyCode: 8 })// simulate backspace keypress
217 | this._input.size = this._inputMinSize
218 | this._input.focus()
219 | this._cancel.style.display = 'none'
220 | this._hideTooltip()
221 | this.fire('search:cancel')
222 | return this
223 | },
224 |
225 | expand: function (toggle) {
226 | toggle = typeof toggle === 'boolean' ? toggle : true
227 | this._input.style.display = 'block'
228 | L.DomUtil.addClass(this._container, 'search-exp')
229 | if (toggle !== false) {
230 | this._input.focus()
231 | this._map.on('dragstart click', this.collapse, this)
232 | }
233 | this.fire('search:expanded')
234 | return this
235 | },
236 |
237 | collapse: function () {
238 | this._hideTooltip()
239 | this.cancel()
240 | this._alert.style.display = 'none'
241 | this._input.blur()
242 | if (this.options.collapsed) {
243 | this._input.style.display = 'none'
244 | this._cancel.style.display = 'none'
245 | L.DomUtil.removeClass(this._container, 'search-exp')
246 | if (this.options.hideMarkerOnCollapse) {
247 | this._map.removeLayer(this._markerSearch)
248 | }
249 | this._map.off('dragstart click', this.collapse, this)
250 | }
251 | this.fire('search:collapsed')
252 | return this
253 | },
254 |
255 | collapseDelayed: function () { // collapse after delay, used on_input blur
256 | const self = this
257 | if (!this.options.autoCollapse) return this
258 | clearTimeout(this.timerCollapse)
259 | this.timerCollapse = setTimeout(function () {
260 | self.collapse()
261 | }, this.options.autoCollapseTime)
262 | return this
263 | },
264 |
265 | collapseDelayedStop: function () {
266 | clearTimeout(this.timerCollapse)
267 | return this
268 | },
269 |
270 | /// /start DOM creations
271 | _createAlert: function (className) {
272 | const alert = L.DomUtil.create('div', className, this._container)
273 | alert.style.display = 'none'
274 |
275 | L.DomEvent
276 | .on(alert, 'click', L.DomEvent.stop, this)
277 | .on(alert, 'click', this.hideAlert, this)
278 |
279 | return alert
280 | },
281 |
282 | _createInput: function (text, className) {
283 | const self = this
284 | const label = L.DomUtil.create('label', className, this._container)
285 | const input = L.DomUtil.create('input', className, this._container)
286 | input.type = 'text'
287 | input.size = this._inputMinSize
288 | input.value = ''
289 | input.autocomplete = 'off'
290 | input.autocorrect = 'off'
291 | input.autocapitalize = 'off'
292 | input.placeholder = text
293 | input.style.display = 'none'
294 | input.role = 'search'
295 | input.id = input.role + input.type + input.size
296 |
297 | label.htmlFor = input.id
298 | label.style.display = 'none'
299 | label.value = text
300 |
301 | L.DomEvent
302 | .disableClickPropagation(input)
303 | .on(input, 'keyup', this._handleKeypress, this)
304 | .on(input, 'paste', function (e) {
305 | setTimeout(function (e) {
306 | self._handleKeypress(e)
307 | }, 10, e)
308 | }, this)
309 | .on(input, 'blur', this.collapseDelayed, this)
310 | .on(input, 'focus', this.collapseDelayedStop, this)
311 |
312 | return input
313 | },
314 |
315 | _createCancel: function (title, className) {
316 | const cancel = L.DomUtil.create('a', className, this._container)
317 | cancel.href = '#'
318 | cancel.title = title
319 | cancel.style.display = 'none'
320 | cancel.innerHTML = '⊗'// imageless(see css)
321 |
322 | L.DomEvent
323 | .on(cancel, 'click', L.DomEvent.stop, this)
324 | .on(cancel, 'click', this.cancel, this)
325 |
326 | return cancel
327 | },
328 |
329 | _createButton: function (title, className) {
330 | const button = L.DomUtil.create('a', className, this._container)
331 | button.href = '#'
332 | button.title = title
333 |
334 | L.DomEvent
335 | .on(button, 'click', L.DomEvent.stop, this)
336 | .on(button, 'click', this._handleSubmit, this)
337 | .on(button, 'focus', this.collapseDelayedStop, this)
338 | .on(button, 'blur', this.collapseDelayed, this)
339 |
340 | return button
341 | },
342 |
343 | _createTooltip: function (className) {
344 | const self = this
345 | const tool = L.DomUtil.create('ul', className, this._container)
346 | tool.style.display = 'none'
347 | L.DomEvent
348 | .disableClickPropagation(tool)
349 | .on(tool, 'blur', this.collapseDelayed, this)
350 | .on(tool, 'wheel', function (e) {
351 | self.collapseDelayedStop()
352 | L.DomEvent.stopPropagation(e)// disable zoom map
353 | }, this)
354 | .on(tool, 'mouseover', function (e) {
355 | self.collapseDelayedStop()
356 | }, this)
357 | return tool
358 | },
359 |
360 | _createTip: function (text, val) { // val is object in recordCache, usually is Latlng
361 | let tip
362 |
363 | if (this.options.buildTip) {
364 | tip = this.options.buildTip.call(this, text, val) // custom tip node or html string
365 | if (typeof tip === 'string') {
366 | const tmpNode = L.DomUtil.create('div')
367 | tmpNode.innerHTML = tip
368 | tip = tmpNode.firstChild
369 | }
370 | } else {
371 | tip = L.DomUtil.create('li', '')
372 | tip.innerHTML = text
373 | }
374 |
375 | L.DomUtil.addClass(tip, 'search-tip')
376 | tip._text = text // value replaced in this._input and used by _autoType
377 |
378 | if (this.options.tipAutoSubmit) {
379 | L.DomEvent
380 | .disableClickPropagation(tip)
381 | .on(tip, 'click', L.DomEvent.stop, this)
382 | .on(tip, 'click', function (e) {
383 | this._input.value = text
384 | this._handleAutoresize()
385 | this._input.focus()
386 | this._hideTooltip()
387 | this._handleSubmit()
388 | }, this)
389 | }
390 |
391 | return tip
392 | },
393 |
394 | /// ///end DOM creations
395 |
396 | _getUrl: function (text) {
397 | return (typeof this.options.url === 'function') ? this.options.url(text) : this.options.url
398 | },
399 |
400 | _defaultFilterData: function (text, records) {
401 | const frecords = {}
402 |
403 | text = text.replace(new RegExp('[.*+?^${}()|[\]\\]','g'), '')
404 | // sanitize remove all special characters
405 |
406 | if (text === '') {
407 | return []
408 | }
409 |
410 | const init = this.options.initial ? '^' : ''
411 | const icase = !this.options.casesensitive ? 'i' : undefined
412 |
413 | const regSearch = new RegExp(init + text, icase)
414 |
415 | for (const key in records) {
416 | if (regSearch.test(key)) {
417 | frecords[key] = records[key]
418 | }
419 | }
420 |
421 | return frecords
422 | },
423 |
424 | showTooltip: function (records) {
425 | this._countertips = 0
426 | this._tooltip.innerHTML = ''
427 | this._tooltip.currentSelection = -1 // inizialized for _handleArrowSelect()
428 |
429 | if (this.options.tooltipLimit) {
430 | for (const key in records) { // fill tooltip
431 | if (this._countertips === this.options.tooltipLimit) {
432 | break
433 | }
434 |
435 | this._countertips++
436 |
437 | this._tooltip.appendChild(this._createTip(key, records[key]))
438 | }
439 | }
440 |
441 | if (this._countertips > 0) {
442 | this._tooltip.style.display = 'block'
443 |
444 | if (this._autoTypeTmp) {
445 | this._autoType()
446 | }
447 |
448 | this._autoTypeTmp = this.options.autoType// reset default value
449 | } else {
450 | this._hideTooltip()
451 | }
452 |
453 | this._tooltip.scrollTop = 0
454 |
455 | return this._countertips
456 | },
457 |
458 | _hideTooltip: function () {
459 | this._tooltip.style.display = 'none'
460 | this._tooltip.innerHTML = ''
461 | return 0
462 | },
463 |
464 | _defaultFormatData: function (json) { // default callback for format data to indexed data
465 | const self = this
466 | const propName = this.options.propertyName
467 | const propLoc = this.options.propertyLoc
468 | const jsonret = {}
469 |
470 | if (L.Util.isArray(propLoc)) {
471 | for (const i in json) {
472 | jsonret[self._getPath(json[i], propName)] = L.latLng(self._getPath(json[i], propLoc[0]), self._getPath(json[i], propLoc[1]))
473 | }
474 | } else {
475 | for (const i in json) {
476 | jsonret[self._getPath(json[i], propName)] = L.latLng(self._getPath(json[i], propLoc))
477 | }
478 | }
479 | // TODO throw new Error("propertyName '"+propName+"' not found in JSON data");
480 | return jsonret
481 | },
482 |
483 | _recordsFromJsonp: function (text, callAfter) { // extract searched records from remote jsonp service
484 | L.Control.Search.callJsonp = callAfter
485 | const script = L.DomUtil.create('script', 'leaflet-search-jsonp', document.getElementsByTagName('body')[0])
486 | const url = L.Util.template(this._getUrl(text) + '&' + this.options.jsonpParam + '=L.Control.Search.callJsonp', { s: text }) // parsing url
487 | // rnd = '&_='+Math.floor(Math.random()*10000);
488 | // TODO add rnd param or randomize callback name! in recordsFromJsonp
489 | script.type = 'text/javascript'
490 | script.src = url
491 | return { abort: function () { script.parentNode.removeChild(script) } }
492 | },
493 |
494 | _recordsFromAjax: function (text, callAfter) { // Ajax request
495 | /*
496 | if (window.XMLHttpRequest === undefined) {
497 | window.XMLHttpRequest = function () {
498 | try {
499 | return new ActiveXObject('Microsoft.XMLHTTP.6.0')
500 | } catch (e1) {
501 | try {
502 | return new ActiveXObject('Microsoft.XMLHTTP.3.0')
503 | } catch (e2) {
504 | throw new Error('XMLHttpRequest is not supported')
505 | }
506 | }
507 | }
508 | }
509 | const IE8or9 = (L.Browser.ie && !window.atob && document.querySelector)
510 | const request = IE8or9 ? new XDomainRequest() : new XMLHttpRequest()
511 | */
512 | let request
513 |
514 | try {
515 | request = new window.XMLHttpRequest()
516 | } catch (e) {
517 | throw new Error('XMLHttpRequest is not supported')
518 | }
519 | const url = L.Util.template(this._getUrl(text), { s: text })
520 |
521 | // rnd = '&_='+Math.floor(Math.random()*10000);
522 | // TODO add rnd param or randomize callback name! in recordsFromAjax
523 |
524 | request.open('GET', url)
525 |
526 | request.onload = function () {
527 | callAfter(JSON.parse(request.responseText))
528 | }
529 | request.onreadystatechange = function () {
530 | if (request.readyState === 4 && request.status === 200) {
531 | this.onload()
532 | }
533 | }
534 |
535 | request.send()
536 | return request
537 | },
538 |
539 | _searchInLayer: function (layer, retRecords, propName, baseProp = 'options') {
540 | const self = this; let loc
541 |
542 | if (layer instanceof L.Control.Search.Marker) return
543 |
544 | if (layer instanceof L.Marker || layer instanceof L.CircleMarker) {
545 | if (self._getPath(layer.options, propName)) {
546 | loc = layer.getLatLng()
547 | loc.layer = layer
548 | retRecords[self._getPath(layer.options, propName)] = loc
549 | } else if (self._getPath(layer.feature.properties, propName)) {
550 | loc = layer.getLatLng()
551 | loc.layer = layer
552 | retRecords[self._getPath(layer.feature.properties, propName)] = loc
553 | } else {
554 | console.warn(`propertyName '${propName}' not found in marker`);
555 | }
556 | } else if (layer instanceof L.Path || layer instanceof L.Polyline || layer instanceof L.Polygon) {
557 | if (self._getPath(layer.options, propName)) {
558 | loc = layer.getBounds().getCenter()
559 | loc.layer = layer
560 | retRecords[self._getPath(layer.options, propName)] = loc
561 | } else if (self._getPath(layer.feature.properties, propName)) {
562 | loc = layer.getBounds().getCenter()
563 | loc.layer = layer
564 | retRecords[self._getPath(layer.feature.properties, propName)] = loc
565 | } else {
566 | console.warn(`propertyName '${propName}' not found in shape`);
567 | }
568 | } else if (Object.prototype.hasOwnProperty.call(layer, 'feature')) { // GeoJSON
569 | if (Object.prototype.hasOwnProperty.call(layer.feature.properties, propName)) {
570 | if (layer.getLatLng && typeof layer.getLatLng === 'function') {
571 | loc = layer.getLatLng()
572 | loc.layer = layer
573 | retRecords[layer.feature.properties[propName]] = loc
574 | } else if (layer.getBounds && typeof layer.getBounds === 'function') {
575 | loc = layer.getBounds().getCenter()
576 | loc.layer = layer
577 | retRecords[layer.feature.properties[propName]] = loc
578 | } else {
579 | console.warn(`Unknown type of Layer`);
580 | }
581 | } else {
582 | console.warn(`propertyName '${propName}' not found in feature`);
583 | }
584 | } else if (layer instanceof L.LayerGroup) {
585 | layer.eachLayer(function (layer) {
586 | self._searchInLayer(layer, retRecords, propName)
587 | })
588 | }
589 | },
590 |
591 | _recordsFromLayer: function () { // return table: key,value from layer
592 | const self = this
593 | const retRecords = {}
594 | const propName = this.options.propertyName
595 |
596 | this._layer.eachLayer(function (layer) {
597 | self._searchInLayer(layer, retRecords, propName)
598 | })
599 |
600 | return retRecords
601 | },
602 |
603 | _autoType: function () {
604 | // TODO implements autype without selection(useful for mobile device)
605 |
606 | const start = this._input.value.length
607 | const firstRecord = this._tooltip.firstChild ? this._tooltip.firstChild._text : ''
608 | const end = firstRecord.length
609 |
610 | if (firstRecord.indexOf(this._input.value) === 0) { // If prefix match
611 | this._input.value = firstRecord
612 | this._handleAutoresize()
613 |
614 | if (this._input.createTextRange) {
615 | const selRange = this._input.createTextRange()
616 | selRange.collapse(true)
617 | selRange.moveStart('character', start)
618 | selRange.moveEnd('character', end)
619 | selRange.select()
620 | } else if (this._input.setSelectionRange) {
621 | this._input.setSelectionRange(start, end)
622 | } else if (this._input.selectionStart) {
623 | this._input.selectionStart = start
624 | this._input.selectionEnd = end
625 | }
626 | }
627 | },
628 |
629 | _hideAutoType: function () { // deselect text:
630 | let sel
631 | if ((sel = this._input.selection) && sel.empty) {
632 | sel.empty()
633 | } else if (this._input.createTextRange) {
634 | sel = this._input.createTextRange()
635 | sel.collapse(true)
636 | const end = this._input.value.length
637 | sel.moveStart('character', end)
638 | sel.moveEnd('character', end)
639 | sel.select()
640 | } else {
641 | if (this._input.getSelection) {
642 | this._input.getSelection().removeAllRanges()
643 | }
644 | this._input.selectionStart = this._input.selectionEnd
645 | }
646 | },
647 |
648 | _handleKeypress: function (e) { // run _input keyup event
649 | const self = this
650 |
651 | switch (e.keyCode) {
652 | case 27: /* Esc */
653 | this.collapse()
654 | break
655 | case 13: /* Enter */
656 | if (this._countertips === 1 || (this.options.firstTipSubmit && this._countertips > 0)) {
657 | if (this._tooltip.currentSelection === -1) {
658 | this._handleArrowSelect(1)
659 | }
660 | }
661 | this._handleSubmit() // do search
662 | break
663 | case 38: /* Up */
664 | this._handleArrowSelect(-1)
665 | break
666 | case 40: /* Down */
667 | this._handleArrowSelect(1)
668 | break
669 | case 45: /* Insert */
670 | case 46: /* Delete */
671 | this._autoTypeTmp = false// disable temporarily autoType
672 | break
673 | case 37: /* Left */
674 | case 39: /* Right */
675 | case 16: /* Shift */
676 | case 17: /* Ctrl */
677 | case 35: /* End */
678 | case 36: /* Home */
679 | break
680 | default: /* All keys */
681 | if (this._input.value.length) {
682 | this._cancel.style.display = 'block'
683 | }
684 | else {
685 | this._cancel.style.display = 'none'
686 | }
687 |
688 | if (this._input.value.length >= this.options.minLength) {
689 | clearTimeout(this.timerKeypress) // cancel last search request while type in
690 | this.timerKeypress = setTimeout(function () { // delay before request, for limit jsonp/ajax request
691 | self._fillRecordsCache()
692 | }, this.options.delayType)
693 | } else { this._hideTooltip() }
694 | }
695 |
696 | this._handleAutoresize()
697 | },
698 |
699 | searchText: function (text) {
700 | const code = text.charCodeAt(text.length)
701 |
702 | this._input.value = text
703 |
704 | this._input.style.display = 'block'
705 | L.DomUtil.addClass(this._container, 'search-exp')
706 |
707 | this._autoTypeTmp = false
708 |
709 | this._handleKeypress({ keyCode: code })
710 | },
711 |
712 | _fillRecordsCache: function () {
713 | const self = this
714 | const inputText = this._input.value; let records
715 |
716 | if (this._curReq && this._curReq.abort) { this._curReq.abort() }
717 | // abort previous requests
718 |
719 | L.DomUtil.addClass(this._container, 'search-load')
720 |
721 | if (this.options.layer) {
722 | // TODO _recordsFromLayer must return array of objects, formatted from _formatData
723 | this._recordsCache = this._recordsFromLayer()
724 |
725 | records = this._filterData(this._input.value, this._recordsCache)
726 |
727 | this.showTooltip(records)
728 |
729 | L.DomUtil.removeClass(this._container, 'search-load')
730 | } else {
731 | if (this.options.sourceData) { this._retrieveData = this.options.sourceData } else if (this.options.url) { // jsonp or ajax
732 | this._retrieveData = this.options.jsonpParam ? this._recordsFromJsonp : this._recordsFromAjax
733 | }
734 |
735 | this._curReq = this._retrieveData.call(this, inputText, function (data) {
736 | self._recordsCache = self._formatData(self, data)
737 |
738 | // TODO refact!
739 | if (self.options.sourceData) { records = self._filterData(self._input.value, self._recordsCache) } else { records = self._recordsCache }
740 |
741 | self.showTooltip(records)
742 |
743 | L.DomUtil.removeClass(self._container, 'search-load')
744 | })
745 | }
746 | },
747 |
748 | _handleAutoresize: function () {
749 | let maxWidth
750 |
751 | if (this._input.style.maxWidth !== this._map._container.offsetWidth) {
752 | maxWidth = this._map._container.clientWidth
753 |
754 | // other side margin + padding + width border + width search-button + width search-cancel
755 | maxWidth -= 10 + 20 + 1 + 30 + 22
756 |
757 | this._input.style.maxWidth = maxWidth.toString() + 'px'
758 | }
759 |
760 | if (this.options.autoResize && (this._container.offsetWidth + 20 < this._map._container.offsetWidth)) {
761 | this._input.size = this._input.value.length < this._inputMinSize ? this._inputMinSize : this._input.value.length
762 | }
763 | },
764 |
765 | _handleArrowSelect: function (velocity) {
766 | const searchTips = this._tooltip.hasChildNodes() ? this._tooltip.childNodes : []
767 |
768 | for (let i = 0; i < searchTips.length; i++) {
769 | L.DomUtil.removeClass(searchTips[i], 'search-tip-select')
770 | }
771 |
772 | if ((velocity === 1) && (this._tooltip.currentSelection >= (searchTips.length - 1))) { // If at end of list.
773 | L.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select')
774 | } else if ((velocity === -1) && (this._tooltip.currentSelection <= 0)) { // Going back up to the search box.
775 | this._tooltip.currentSelection = -1
776 | } else if (this._tooltip.style.display !== 'none') {
777 | this._tooltip.currentSelection += velocity
778 |
779 | L.DomUtil.addClass(searchTips[this._tooltip.currentSelection], 'search-tip-select')
780 |
781 | this._input.value = searchTips[this._tooltip.currentSelection]._text
782 |
783 | // scroll:
784 | const tipOffsetTop = searchTips[this._tooltip.currentSelection].offsetTop
785 |
786 | if (tipOffsetTop + searchTips[this._tooltip.currentSelection].clientHeight >= this._tooltip.scrollTop + this._tooltip.clientHeight) {
787 | this._tooltip.scrollTop = tipOffsetTop - this._tooltip.clientHeight + searchTips[this._tooltip.currentSelection].clientHeight
788 | } else if (tipOffsetTop <= this._tooltip.scrollTop) {
789 | this._tooltip.scrollTop = tipOffsetTop
790 | }
791 | }
792 | },
793 |
794 | _handleSubmit: function () { // button and tooltip click and enter submit
795 | this._hideAutoType()
796 |
797 | this.hideAlert()
798 | this._hideTooltip()
799 |
800 | if (this._input.style.display === 'none') { // on first click show _input only
801 | this.expand()
802 | } else {
803 | if (this._input.value === '') { // hide _input only
804 | this.collapse()
805 | } else {
806 | const loc = this._getLocation(this._input.value)
807 |
808 | if (!loc) {
809 | this.showAlert()
810 | } else {
811 | this.showLocation(loc, this._input.value)
812 | this.fire('search:locationfound', {
813 | latlng: loc,
814 | text: this._input.value,
815 | layer: loc.layer ? loc.layer : null
816 | })
817 | }
818 | }
819 | }
820 | },
821 |
822 | _getLocation: function (key) { // extract latlng from _recordsCache
823 | if (Object.prototype.hasOwnProperty.call(this._recordsCache, key)) {
824 | return this._recordsCache[key]
825 | } else {
826 | return false
827 | }
828 | },
829 |
830 | _defaultMoveToLocation: function (latlng, title, map) {
831 | if (this.options.zoom) {
832 | this._map.setView(latlng, this.options.zoom)
833 | } else {
834 | this._map.panTo(latlng)
835 | }
836 | },
837 |
838 | showLocation: function (latlng, title) { // set location on map from _recordsCache
839 | const self = this
840 |
841 | self._map.once('moveend zoomend', function (e) {
842 | if (self._markerSearch) {
843 | self._markerSearch.addTo(self._map).setLatLng(latlng)
844 | }
845 | })
846 |
847 | self._moveToLocation(latlng, title, self._map)
848 | // FIXME autoCollapse option hide self._markerSearch before visualized!!
849 | if (self.options.autoCollapse) { self.collapse() }
850 |
851 | return self
852 | }
853 | })
854 |
855 | L.Control.Search.Marker = L.Marker.extend({
856 |
857 | includes: L.version[0] === '1' ? L.Evented.prototype : L.Mixin.Events,
858 |
859 | options: {
860 | icon: new L.Icon.Default(),
861 | animate: true,
862 | circle: {
863 | radius: 10,
864 | weight: 3,
865 | color: '#e03',
866 | stroke: true,
867 | fill: false
868 | }
869 | },
870 |
871 | initialize: function (latlng, options) {
872 | L.setOptions(this, options)
873 |
874 | if (options.icon === true) { options.icon = new L.Icon.Default() }
875 |
876 | L.Marker.prototype.initialize.call(this, latlng, options)
877 |
878 | if (L.Control.Search.prototype._isObject(this.options.circle)) { this._circleLoc = new L.CircleMarker(latlng, this.options.circle) }
879 | },
880 |
881 | onAdd: function (map) {
882 | L.Marker.prototype.onAdd.call(this, map)
883 | if (this._circleLoc) {
884 | map.addLayer(this._circleLoc)
885 | if (this.options.animate) { this.animate() }
886 | }
887 | },
888 |
889 | onRemove: function (map) {
890 | L.Marker.prototype.onRemove.call(this, map)
891 | if (this._circleLoc) { map.removeLayer(this._circleLoc) }
892 | },
893 |
894 | setLatLng: function (latlng) {
895 | L.Marker.prototype.setLatLng.call(this, latlng)
896 | if (this._circleLoc) { this._circleLoc.setLatLng(latlng) }
897 | return this
898 | },
899 |
900 | _initIcon: function () {
901 | if (this.options.icon) { L.Marker.prototype._initIcon.call(this) }
902 | },
903 |
904 | _removeIcon: function () {
905 | if (this.options.icon) { L.Marker.prototype._removeIcon.call(this) }
906 | },
907 |
908 | animate: function () {
909 | // TODO refact animate() more smooth! like this: http://goo.gl/DDlRs
910 | if (this._circleLoc) {
911 | const circle = this._circleLoc
912 | const tInt = 200 // time interval
913 | const ss = 5 // frames
914 | let mr = parseInt(circle._radius / ss)
915 | const oldrad = this.options.circle.radius
916 | let newrad = circle._radius * 2
917 | let acc = 0
918 |
919 | circle._timerAnimLoc = setInterval(function () {
920 | acc += 0.5
921 | mr += acc // adding acceleration
922 | newrad -= mr
923 |
924 | circle.setRadius(newrad)
925 |
926 | if (newrad < oldrad) {
927 | clearInterval(circle._timerAnimLoc)
928 | circle.setRadius(oldrad)// reset radius
929 | // if(typeof afterAnimCall == 'function')
930 | // afterAnimCall();
931 | // TODO use create event 'animateEnd' in L.Control.Search.Marker
932 | }
933 | }, tInt)
934 | }
935 |
936 | return this
937 | }
938 | })
939 |
940 | L.Map.addInitHook(function () {
941 | if (this.options.searchControl) {
942 | this.searchControl = L.control.search(this.options.searchControl)
943 | this.addControl(this.searchControl)
944 | }
945 | })
946 |
947 | L.control.search = function (options) {
948 | return new L.Control.Search(options)
949 | }
950 |
951 | return L.Control.Search
952 | })
953 |
--------------------------------------------------------------------------------
/src/leaflet-search.mobile.css:
--------------------------------------------------------------------------------
1 |
2 | /* SEARCH */
3 | .leaflet-control.leaflet-control-search {
4 | z-index:2000;
5 | }
6 | .leaflet-control-search .search-input {
7 | display:block;
8 | float:left;
9 | background: #fff;
10 | border:1px solid #666;
11 | border-radius:2px;
12 | height:24px;
13 | font-size:1.25em;
14 | padding:0 .125em;
15 | margin:3px;
16 | padding-right:30px;
17 | }
18 | .leaflet-control-search .search-button:hover,
19 | .leaflet-control-search .search-button {
20 | background-image: url('../images/search-icon-mobile.png');
21 | -webkit-border-radius: 4px;
22 | border-radius: 4px;
23 | background-position: 1px 1px;
24 | width:32px;
25 | height:32px;
26 | }
27 | .leaflet-control-search.search-load .search-input {
28 | background: url('../images/loader.gif') no-repeat center right #fff;
29 | }
30 | .leaflet-control-search .search-cancel {
31 | background-image: url('../images/search-icon-mobile.png');
32 | -webkit-border-radius: 4px;
33 | border-radius: 4px;
34 | background-position: 0px -62px;
35 | width:26px;
36 | height:26px;
37 | right:34px;
38 | margin:3px;
39 | }
40 | .leaflet-control-search .search-tooltip {
41 | max-height:142px;/*(.search-tip height * 5)*/
42 | }
43 | .leaflet-control-search .search-tip {
44 | font-size:1em;
45 | margin:2px;
46 | padding:2px;
47 | display:block;
48 | color:black;
49 | background: rgba(255,255,255,0.8);
50 | border-radius:.25em;
51 | text-decoration:none;
52 | white-space:nowrap;
53 | vertical-align:center;
54 | }
55 | .leaflet-control-search .search-tip .climbo-icon-mini {
56 | float:right;
57 | display:block;
58 | white-space:nowrap;
59 | }
60 | .leaflet-control-search .search-button:hover,
61 | .leaflet-control-search .search-tip-select,
62 | .leaflet-control-search .search-tip:hover {
63 | background-color: #fff;
64 | }
65 | .leaflet-control-search .search-alert {
66 | font-size:1.2em;
67 | }
--------------------------------------------------------------------------------