├── .gitignore
├── examples
├── bar.png
├── loader.gif
├── search.php
├── style.css
├── jsonp.html
├── simple.html
├── jquery.html
├── fixed-remote-data.html
├── polygons.html
├── overpass.html
├── cluster.html
├── htmlcolors.txt
└── calldata.html
├── package.json
├── license.txt
├── Gruntfile.js
├── README.md
├── index.html
├── dist
├── leaflet-layerjson.min.js
└── leaflet-layerjson.src.js
└── src
└── leaflet-layerjson.js
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/*
2 |
--------------------------------------------------------------------------------
/examples/bar.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-layerJSON/HEAD/examples/bar.png
--------------------------------------------------------------------------------
/examples/loader.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/stefanocudini/leaflet-layerJSON/HEAD/examples/loader.gif
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "leaflet-layerjson",
3 | "version": "0.3.4",
4 | "description": "A Dynamic Leaflet Layer that load/filter dinamically JSON data from Ajax/Jsonp, with caching",
5 | "repository": {
6 | "type": "git",
7 | "url": "git@github.com:stefanocudini/leaflet-layerjson.git"
8 | },
9 | "homepage": "https://opengeo.tech/maps/leaflet-layerjson/",
10 | "author": {
11 | "name": "Stefano Cudini",
12 | "email": "stefano.cudini@gmail.com",
13 | "url": "https://opengeo.tech/"
14 | },
15 | "license": "MIT",
16 | "keywords": [
17 | "gis",
18 | "map",
19 | "leaflet"
20 | ],
21 | "main": "dist/leaflet-layerjson.min.js",
22 | "dependencies": {
23 | "leaflet": "*"
24 | },
25 | "devDependencies": {
26 | "grunt": "~0.4.2",
27 | "grunt-cli": "~0.1.11",
28 | "grunt-contrib-uglify": "~0.2.7",
29 | "grunt-contrib-concat": "~0.3.0",
30 | "grunt-contrib-clean": "~0.5.0",
31 | "grunt-contrib-jshint": "~0.7.2",
32 | "grunt-contrib-watch": "~0.5.3"
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/license.txt:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2013-2020 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 |
--------------------------------------------------------------------------------
/examples/search.php:
--------------------------------------------------------------------------------
1 | $row[0], 'color'=>$row[1], 'id'=>$m);
46 | fclose($cf);
47 | }
48 | return $colors;
49 | }
50 | ?>
51 |
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 | 'use strict';
2 |
3 | module.exports = function(grunt) {
4 |
5 | grunt.loadNpmTasks('grunt-contrib-clean');
6 | grunt.loadNpmTasks('grunt-contrib-concat');
7 | grunt.loadNpmTasks('grunt-contrib-jshint');
8 | grunt.loadNpmTasks('grunt-contrib-uglify');
9 | grunt.loadNpmTasks('grunt-contrib-watch');
10 |
11 | grunt.initConfig({
12 | pkg: grunt.file.readJSON('package.json'),
13 | meta: {
14 | banner:
15 | '/* \n'+
16 | ' * Leaflet JSON Layer v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %> \n'+
17 | ' * \n'+
18 | ' * Copyright <%= grunt.template.today("yyyy") %> <%= pkg.author.name %> \n'+
19 | ' * <%= pkg.author.email %> \n'+
20 | ' * <%= pkg.author.url %> \n'+
21 | ' * \n'+
22 | ' * Licensed under the <%= pkg.license %> license. \n'+
23 | ' * \n'+
24 | ' * Demo: \n'+
25 | ' * <%= pkg.homepage %> \n'+
26 | ' * \n'+
27 | ' * Source: \n'+
28 | ' * <%= pkg.repository.url %> \n'+
29 | ' * \n'+
30 | ' */\n'
31 | },
32 | clean: {
33 | dist: {
34 | src: ['dist/*']
35 | }
36 | },
37 | jshint: {
38 | options: {
39 | globals: {
40 | console: true,
41 | module: true
42 | },
43 | "-W099": true, //ignora tabs e space warning
44 | "-W033": true,
45 | "-W044": true, //ignore regexp
46 | "-W061": true //ignore eval in getAjax()
47 | },
48 | files: ['src/*.js']
49 | },
50 | concat: {
51 | options: {
52 | banner: '<%= meta.banner %>'
53 | },
54 | dist: {
55 | files: {
56 | 'dist/leaflet-layerjson.src.js': ['src/leaflet-layerjson.js']
57 | }
58 | }
59 | },
60 | uglify: {
61 | options: {
62 | banner: '<%= meta.banner %>'
63 | },
64 | dist: {
65 | files: {
66 | 'dist/leaflet-layerjson.min.js': ['dist/leaflet-layerjson.src.js']
67 | }
68 | }
69 | },
70 | watch: {
71 | dist: {
72 | options: { livereload: true },
73 | files: ['src/*','examples/*.html'],
74 | tasks: ['clean','concat','jshint']
75 | }
76 | }
77 | });
78 |
79 | grunt.registerTask('default', [
80 | 'clean',
81 | 'concat',
82 | 'jshint',
83 | 'uglify'
84 | ]);
85 |
86 | };
--------------------------------------------------------------------------------
/examples/style.css:
--------------------------------------------------------------------------------
1 | body {
2 | background: #def;
3 | color:#456;
4 | }
5 |
6 | h2,h3,h4,h5 {margin:6px 0}
7 | a {text-decoration: none}
8 | p {margin:0 0 20px 0}
9 | ul {
10 | font-size:.85em;
11 | margin:0 0 10px 0;
12 | padding:0;
13 | }
14 | li {
15 | margin:0 0 2px 18px;
16 | }
17 | #map {
18 | border:2px solid #669;
19 | width:800px;
20 | height:340px;
21 | }
22 | #post-it {
23 | width:120px;
24 | height:120px;
25 | padding:1em;
26 | float:left;
27 | background:#fbf5bf;
28 | border:1px solid #c6bb58;
29 | box-shadow: 2px 2px 6px #999;
30 | color:#666;
31 | }
32 | #arrow {
33 | display: block;
34 | line-height: 380px;
35 | font-size: 80px;
36 | float: left;
37 | text-align: center;
38 | width: 50px;
39 | }
40 | #data {
41 | width:800px;
42 | height:150px;
43 | background:#eee;
44 | border:2px solid #669;
45 | padding:2px;
46 | font-size:.85em;
47 | overflow:scroll;
48 | margin:0;
49 | }
50 | #content,#refs {
51 | float:left;
52 | margin-right:1em;
53 | }
54 | #copy {
55 | position:fixed;
56 | z-index:1000;
57 | right:150px;
58 | top:-6px;
59 | font-style:italic;
60 | font-size:.85em;
61 | padding:2px 8px;
62 | background: #ccc;
63 | border: 2px solid #3e5585;
64 | border-radius:.7em;
65 | opacity: 0.8;
66 | }
67 | #copy a {
68 | color:#285585
69 | }
70 | #ribbon {
71 | position: absolute;
72 | top: 0;
73 | right: 0;
74 | border: 0;
75 | filter: alpha(opacity=80);
76 | -khtml-opacity: .8;
77 | -moz-opacity: .8;
78 | opacity: .8;
79 | z-index: 2000;
80 | }
81 | #comments {
82 | clear:both;
83 | }
84 | pre {
85 | background-color: #fff;
86 | color:#333;
87 | line-height: 1.5em;
88 | padding:10px;
89 | float: left;
90 | font-size: 12px;
91 | border: 1px solid #aaa;
92 | box-shadow:inset 2px 2px 4px #ccc
93 | }
94 | #loader {
95 | display: none;
96 | position: absolute;
97 | top:0;
98 | bottom: 0;
99 | left: 0;
100 | right: 0;
101 | background: url('examples/loader.gif') center center no-repeat rgba(255,255,255,0.5);
102 | }
103 |
104 |
--------------------------------------------------------------------------------
/examples/jsonp.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | JSONP Example: get data from 3rd party JSONP service and transform in markers
13 |
14 |
15 |
16 | Data offer by
17 |
nominatim.osm.org
18 |
19 |
20 |
21 |
44 |
45 | Github
46 |
47 |
48 |
49 |
--------------------------------------------------------------------------------
/examples/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | AJAX Example: load json data by Ajax request
13 |
14 | For simplicity in this example the server side distributes data always random irrespective of the coordinates, in a real case much more likely server
15 | side there will be a spatial database that returns data inside requested bounding box .
16 |
17 |
21 |
22 |
23 |
39 |
40 | Github
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/jquery.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | jQuery Example: load json data by Ajax using jQuery
13 |
14 | For simplicity in this example the server side distributes data always random irrespective of the coordinates, in a real case much more likely server
15 | side there will be a spatial database that returns data inside requested bounding box .
16 |
17 |
21 |
22 |
23 |
24 |
25 |
53 |
54 | Github
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/examples/fixed-remote-data.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Remote Fixed Data: load data from remote static JSON
13 |
14 |
15 |
16 |
17 | Data offer by
unhcr.org API
18 |
19 | Population settlements
20 |
21 |
22 |
23 |
63 |
64 | Github
65 |
66 |
67 |
68 |
--------------------------------------------------------------------------------
/examples/polygons.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | Example: Load Paths and Polygons
13 |
14 |
15 |
16 |
17 | Using:
Overpass API
18 |
19 | Service offer by
20 |
overpass-api.de
21 |
22 | Data offer by
OpenStreetMap.org
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
67 |
68 | Github
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/examples/overpass.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 | Example: Show Bars in Rome
14 |
15 | Using:
Overpass API
16 |
17 | Service offer by
18 |
overpass-api.de
19 |
20 | Data offer by
OpenStreetMap.org
21 |
22 |
26 |
27 |
28 |
69 |
70 | Github
71 |
72 |
73 |
74 |
--------------------------------------------------------------------------------
/examples/cluster.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | Example: Show Bars in Rome
17 |
18 | Using:
Overpass API
19 |
20 | Service offer by
21 |
overpass-api.de
22 |
23 | Data offer by
OpenStreetMap.org
24 |
25 |
29 |
30 |
31 |
32 |
33 |
74 |
75 | Github
76 |
77 |
78 |
79 |
--------------------------------------------------------------------------------
/examples/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 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Leaflet JSON Layer
2 | ============
3 |
4 | [](https://badge.fury.io/js/leaflet-layerjson)
5 |
6 | Simple way for transform any JSON data source in a Leaflet Layer!
7 |
8 | A Dynamic Leaflet Layer that load JSON data in layer in the form of markers with attributes
9 |
10 | and minimize remote requests with caching system
11 |
12 | Copyright 2023 Stefano Cudini
13 |
14 | If this project helped your work help me to keep this alive by [Paypal **DONATION ❤**](https://www.paypal.me/stefanocudini)
15 |
16 | Tested in Leaflet 0.7 and 1.1
17 |
18 | # Options
19 | | Option | Data | Description |
20 | | ------------- | --------| ----------------------------------------- |
21 | | url | String | remote url |
22 | | jsonpParam | String | callback parameter name for jsonp request append to url |
23 | | jsonpParam | String | callback parameter name for jsonp request append to url |
24 | | callData | Function | custom function for data source, params: (req: url|bbox, callback: func), return {abort: func} or jQuery jqXHR Object |
25 | | **Filtering** | | |
26 | | propertyItems | String | json property used contains data items |
27 | | propertyLoc | String | json property used as Latlng of marker, if is array: *['lat','lon']* select double fields |
28 | | locAsGeoJSON | String | interpret location data as [lon, lat] value pair instead of [lat, lon] |
29 | | propertyTitle | String | json property used as title in marker |
30 | | filterData | Function | function for pre-filter data |
31 | | **Rendering** | | |
32 | | dataToMarker | Function | function that will be used for creating markers from json points |
33 | | layerTarget | L.Layer | pre-existing layer to add markers(*L.LayerGroup*, *L.MarkerClusterGroup*) |
34 | | buildPopup | Function | function popup builder |
35 | | optsPopup | String | popup options |
36 | | buildIcon | Function | function icon builder |
37 | | **Caching** | | |
38 | | minZoom | Number | min zoom for call data |
39 | | caching | Boolean | remote requests caching |
40 | | cacheId | Function | function to generate id used to uniquely identify data items in cache |
41 | | minShift | Number | min shift for update data(in meters) |
42 | | precision | Number | number of digit send to server for lat,lng precision |
43 | | updateOutBounds| String | request new data only if current bounds higher than last bounds |
44 | | updateMarkers | Boolean | update all markers in map to last results of callData |
45 |
46 | # Events
47 | | Event | Data | Description |
48 | | ---------------------- | ---------------------- | ----------------------------------------- |
49 | | 'dataloading' | {req: url|bbox} | fired before ajax/jsonp request, req is bbox if url option is null |
50 | | 'dataloaded' | {data: json} | fired on ajax/jsonp request success |
51 |
52 | # Usage
53 |
54 | ```
55 | var l = new L.LayerJSON({url: "search.php?lat1={lat1}&lat2={lat2}&lon1={lon1}&lon2={lon2}" });
56 | map.addLayer(l);
57 | ```
58 |
59 | # Where
60 |
61 | **Demos:**
62 |
63 | [https://opengeo.tech/maps/leaflet-layerjson](https://opengeo.tech/maps/leaflet-layerjson/)
64 |
65 | **Source:**
66 |
67 | [Github](https://github.com/stefanocudini/leaflet-layerjson)
68 |
69 | [Atmosphere](https://atmosphere.meteor.com/package/leaflet-layerjson)
70 |
71 |
72 | # Build
73 |
74 | This plugin support [Grunt](https://gruntjs.com/) for building process.
75 | Therefore the deployment require [NPM](https://npmjs.org/) installed in your system.
76 |
77 | After you've made sure to have npm working, run this in command line:
78 | ```
79 | npm install
80 | grunt
81 | ```
82 |
83 |
--------------------------------------------------------------------------------
/examples/calldata.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
20 |
21 |
22 |
23 | Data by Callback Example: load data from Javascript Static Obejct and highlights nearby markers to the center
24 |
27 |
28 |
29 |
88 |
89 | Github
90 |
91 |
92 |
93 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Leaflet JSON Layer
5 |
6 |
7 |
8 |
9 |
10 |
11 | Leaflet JSON Layer
12 |
13 | Simple way for transform any JSON data source in a Dynamic Leaflet Layer!
14 |
15 |
16 | Other useful stuff for Web Mapping...
17 |
18 |
19 |
20 |
Features
21 |
22 | Load JSON data via AJAX,JSONP or custom function
23 | Request data by bounding box
24 | Caching System for reduce Ajax/Jsonp requests
25 | Pre-filter JSON data from third-party jsonp services
26 | Events : dataloading, dataloaded
27 | Customize popup and icon marker
28 | Customize marker builder
29 | Using exists layer
30 |
31 |
32 |
Options
33 |
34 | min shift before request data
35 | new requests only if comes out of bounds
36 | precision digit for coordinates
37 | and much more...
38 |
39 |
40 |
70 |
71 |
72 |
73 |
Usage
74 |
75 | L.layerJSON({url: "search.php?lat1={lat1}&lat2={lat2}&lon1={lon1}&lon2={lon2}" }).addTo(map);
76 |
77 |
78 |
79 |
80 |
81 |
For questions and bugs I recommend you to
create New Issue on Github repository.
82 | Or to obtain a fast response consult
Official Leaflet community forum .
83 |
84 | This is a micro discussion area for methods of implementation.
85 |
86 |
87 | Github
88 |
89 |
90 |
91 |
--------------------------------------------------------------------------------
/dist/leaflet-layerjson.min.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Leaflet JSON Layer v0.3.3 - 2019-12-27
3 | *
4 | * Copyright 2019 Stefano Cudini
5 | * stefano.cudini@gmail.com
6 | * https://opengeo.tech/
7 | *
8 | * Licensed under the MIT license.
9 | *
10 | * Demo:
11 | * https://opengeo.tech/maps/leaflet-layerjson/
12 | *
13 | * Source:
14 | * git@github.com:stefanocudini/leaflet-layerjson.git
15 | *
16 | */
17 | (function(){L.LayerJSON=L.FeatureGroup.extend({includes:"1"===L.version[0]?L.Evented.prototype:L.Mixin.Events,options:{url:"",jsonpParam:null,callData:null,filterData:null,locAsGeoJSON:!1,propertyItems:"",propertyTitle:"title",propertyLoc:"loc",propertyId:"id",layerTarget:null,dataToMarker:null,buildPopup:null,buildIcon:null,optsPopup:null,minZoom:10,caching:!0,cacheId:null,minShift:1e3,precision:6,updateMarkers:!1,attribution:""},initialize:function(a){L.FeatureGroup.prototype.initialize.call(this,[]),L.Util.setOptions(this,a),this._dataToMarker=this.options.dataToMarker||this._defaultDataToMarker,this._buildIcon=this.options.buildIcon||this._defaultBuildIcon,this._filterData=this.options.filterData||null,this._hashUrl=this.options.url,this._hashUrl?(this._callData=this.getAjax,this.options.jsonpParam&&(this._hashUrl+="&"+this.options.jsonpParam+"=",this._callData=this.getJsonp)):this._callData=this.options.callData,this._curReq=null,this._center=null,this._cacheBounds=null,this._markersCache={}},onAdd:function(a){L.FeatureGroup.prototype.onAdd.call(this,a),this._center=a.getCenter(),this._cacheBounds=a.getBounds(),a.on("moveend zoomend",this._onMove,this),this.update()},onRemove:function(a){a.off("moveend zoomend",this._onMove,this),L.FeatureGroup.prototype.onRemove.call(this,a);for(var b in this._layers)this._layers.hasOwnProperty(b)&&L.FeatureGroup.prototype.removeLayer.call(this,this._layers[b])},getAttribution:function(){return this.options.attribution},addLayer:function(a){return this.options.layerTarget?(this.options.layerTarget.addLayer.call(this.options.layerTarget,a),this.fire("layeradd",{layer:a})):(L.FeatureGroup.prototype.addLayer.call(this,a),this)},removeLayer:function(a){return this.options.layerTarget?(this.options.layerTarget.removeLayer.call(this.options.layerTarget,a),this.fire("layerremove",{layer:a})):(L.FeatureGroup.prototype.removeLayer.call(this,a),this)},clearLayers:function(){var a=this,b=this._map.getBounds();return a.options.layerTarget?a.options.layerTarget.eachLayer(function(c){a._contains(b,c)||(a.options.layerTarget.removeLayer(c),delete a._markersCache[c._cacheId])}):a.eachLayer(function(c){a._contains(b,c)||(a.removeLayer(c),delete a._markersCache[c._cacheId])},a),this},_debouncer:function(a,b){var c;return b=b||300,function(){var d=this,e=arguments;clearTimeout(c),c=setTimeout(function(){a.apply(d,Array.prototype.slice.call(e))},b)}},_getPath:function(a,b){var c=b.split("."),d=c.pop(),e=c.length,f=c[0],g=1;if(e>0)for(;(a=a[f])&&e>g;)f=c[g++];return a?a[d]:void 0},_defaultBuildIcon:function(a,b){return new L.Icon.Default},_defaultDataToMarker:function(a,b){var c=this._getPath(a,this.options.propertyTitle),d=L.Util.extend({icon:this._buildIcon(a,c),title:c},a),e=new L.Marker(b,d),f=null;return this.options.buildPopup&&(f=this.options.buildPopup(a,e))&&e.bindPopup(f,this.options.optsPopup),e},addMarker:function(a){var b,c,d=this.options.propertyLoc;if(L.Util.isArray(d))b=L.latLng(parseFloat(this._getPath(a,d[0])),parseFloat(this._getPath(a,d[1])));else if(this.options.locAsGeoJSON){var e=this._getPath(a,d);b=L.latLng(e[1],e[0])}else b=L.latLng(this._getPath(a,d));return c=this.options.cacheId?this.options.cacheId.call(this,a,b):this.options.propertyId?this._getPath(a,this.options.propertyId):b.lat+""+b.lng+this._getPath(a,this.options.propertyTitle),"undefined"==typeof this._markersCache[c]&&(this._markersCache[c]=this._dataToMarker(a,b),this._markersCache[c]._cacheId=c,this.addLayer(this._markersCache[c])),c},_contains:function(a,b){var c;return b.getLatLng?c=b.getLatLng():b.getBounds&&(c=b.getBounds()),a.contains(c)},_loadCacheToBounds:function(a){for(var b in this._markersCache)this._markersCache[b]&&(this._contains(a,this._markersCache[b])?this.addLayer(this._markersCache[b]):this.removeLayer(this._markersCache[b]))},_onMove:function(a){var b=this._map.getZoom(),c=this._map.getCenter(),d=this._map.getBounds();if(b 0)
168 | while((obj = obj[cur]) && i < len)
169 | cur = parts[i++];
170 |
171 | if(obj)
172 | return obj[last];
173 | },
174 |
175 | _defaultBuildIcon: function(data, title) {
176 | return new L.Icon.Default();
177 | },
178 |
179 | _defaultDataToMarker: function(data, latlng) { //make marker from data
180 |
181 | var title = this._getPath(data, this.options.propertyTitle),
182 | markerOpts = L.Util.extend({icon: this._buildIcon(data,title), title: title }, data),
183 | marker = new L.Marker(latlng, markerOpts ),
184 | htmlPopup = null;
185 |
186 | if(this.options.buildPopup && (htmlPopup = this.options.buildPopup(data, marker)))
187 | marker.bindPopup(htmlPopup, this.options.optsPopup );
188 |
189 | return marker;
190 | },
191 |
192 | addMarker: function(data) {
193 |
194 | var loc, hash, propLoc = this.options.propertyLoc;
195 |
196 | if( L.Util.isArray(propLoc) ) {
197 | loc = L.latLng( parseFloat( this._getPath(data, propLoc[0]) ),
198 | parseFloat( this._getPath(data, propLoc[1]) ) );
199 | }
200 | else {
201 | if (this.options.locAsGeoJSON) {
202 | var lnglat = this._getPath(data, propLoc);
203 | loc = L.latLng( lnglat[1], lnglat[0] );
204 | } else {
205 | loc = L.latLng( this._getPath(data, propLoc) );
206 | }
207 | }
208 |
209 | if(this.options.cacheId) {
210 | hash = this.options.cacheId.call(this, data, loc);
211 | }
212 | else {
213 | if(this.options.propertyId)
214 | hash = this._getPath(data, this.options.propertyId);
215 | else
216 | hash = loc.lat+''+loc.lng+''+this._getPath(data, this.options.propertyTitle);
217 | }
218 |
219 | if(typeof this._markersCache[hash] === 'undefined') {
220 | this._markersCache[hash] = this._dataToMarker(data, loc);
221 | this._markersCache[hash]._cacheId = hash;
222 | this.addLayer( this._markersCache[hash] );
223 | }
224 |
225 | return hash;
226 | },
227 |
228 | _contains: function(bounds, el) {
229 | var loc;
230 |
231 | if(el.getLatLng)
232 | loc = el.getLatLng();
233 | else if(el.getBounds)
234 | loc = el.getBounds();
235 |
236 | return bounds.contains(loc);
237 | },
238 |
239 | _loadCacheToBounds: function(bounds) { //show/hide cached markers
240 | for(var i in this._markersCache)
241 | {
242 | if(this._markersCache[i])
243 | {
244 | if(this._contains(bounds, this._markersCache[i]) )
245 | this.addLayer(this._markersCache[i]);
246 | else
247 | this.removeLayer(this._markersCache[i]);
248 | }
249 | }
250 | },
251 |
252 | _onMove: function(e) {
253 | var newZoom = this._map.getZoom(),
254 | newCenter = this._map.getCenter(),
255 | newBounds = this._map.getBounds();
256 |
257 | if(newZoom < this.options.minZoom)
258 | return false;
259 |
260 | if( this.options.minShift && this._center.distanceTo(newCenter) < this.options.minShift )
261 | return false;
262 | else
263 | this._center = newCenter;
264 |
265 | if(this.options.caching) {
266 |
267 | if( this._cacheBounds.contains(newBounds) )
268 | {
269 | this._loadCacheToBounds(newBounds);
270 | return false;
271 | }
272 | else
273 | this._cacheBounds.extend(newBounds);
274 | }
275 | else
276 | this.clearLayers();
277 |
278 | this.update();
279 | },
280 |
281 | update: function() { //populate target layer
282 |
283 | var self = this;
284 |
285 | var prec = self.options.precision,
286 | bb = self._map.getBounds(),
287 | sw = bb.getSouthWest(),
288 | ne = bb.getNorthEast(),
289 | bbox = [
290 | [ parseFloat(sw.lat.toFixed(prec)), parseFloat(sw.lng.toFixed(prec)) ],
291 | [ parseFloat(ne.lat.toFixed(prec)), parseFloat(ne.lng.toFixed(prec)) ]
292 | ];
293 |
294 | if(self._hashUrl) //conver bbox to url string
295 | bbox = L.Util.template(self._hashUrl, {
296 | lat1: bbox[0][0], lon1: bbox[0][1],
297 | lat2: bbox[1][0], lon2: bbox[1][1]
298 | });
299 |
300 | if(self._curReq && self._curReq.abort)
301 | self._curReq.abort(); //prevent parallel requests
302 |
303 |
304 | self.fire('dataloading', {req: bbox });
305 | self._curReq = self._callData(bbox, function(json) {
306 |
307 | //console.log('callData',json)
308 |
309 | self._curReq = null;
310 |
311 | if(self._filterData)
312 | json = self._filterData(json);
313 |
314 | if(self.options.propertyItems)
315 | json = self._getPath(json, self.options.propertyItems);
316 |
317 | self.fire('dataloaded', {data: json});
318 |
319 | self.updateMarkers(json);
320 | });
321 | },
322 |
323 | updateMarkers: function(json) {
324 | var jsonbyhash = {};
325 |
326 | for (var k in json) {
327 | if (!isNaN(parseFloat(k)) && isFinite(k)) {
328 | var h = this.addMarker.call(this, json[k]);
329 | jsonbyhash[h] = 1;
330 | }
331 | }
332 |
333 | if(this.options.updateMarkers) {
334 | for (var c in this._markersCache) {
335 | if(!jsonbyhash[ this._markersCache[c]._cacheId ])
336 | this.removeLayer(this._markersCache[c]);
337 | else
338 | this.addLayer(this._markersCache[c]);
339 | }
340 | }
341 | },
342 |
343 |
344 | /////////////////ajax jsonp methods
345 |
346 | getAjax: function(url, cb) { //default ajax request
347 |
348 | if (window.XMLHttpRequest === undefined) {
349 | window.XMLHttpRequest = function() {
350 | try {
351 | return new ActiveXObject("Microsoft.XMLHTTP.6.0");
352 | }
353 | catch (e1) {
354 | try {
355 | return new ActiveXObject("Microsoft.XMLHTTP.3.0");
356 | }
357 | catch (e2) {
358 | throw new Error("XMLHttpRequest is not supported");
359 | }
360 | }
361 | };
362 | }
363 | var request = new XMLHttpRequest();
364 | request.open('GET', url);
365 | request.onreadystatechange = function() {
366 | var response = {};
367 | if (request.readyState === 4 && request.status === 200) {
368 | try {
369 | if(window.JSON)
370 | response = JSON.parse(request.responseText);
371 | else
372 | response = eval("("+ request.responseText + ")");
373 | } catch(err) {
374 | response = {};
375 | throw new Error('Ajax response is not JSON');
376 | }
377 | cb(response);
378 | }
379 | };
380 | request.send();
381 | return request;
382 | },
383 |
384 | getJsonp: function(url, cb) { //extract searched records from remote jsonp service
385 | var body = document.getElementsByTagName('body')[0],
386 | script = L.DomUtil.create('script','leaflet-layerjson-jsonp', body );
387 |
388 | //JSONP callback
389 | L.LayerJSON.callJsonp = function(data) {
390 | cb(data);
391 | body.removeChild(script);
392 | }
393 | script.type = 'text/javascript';
394 | script.src = url+'L.LayerJSON.callJsonp';
395 | return {
396 | abort: function() {
397 | script.parentNode.removeChild(script);
398 | }
399 | };
400 | }
401 | });
402 |
403 | L.layerJSON = function (options) {
404 | return new L.LayerJSON(options);
405 | };
406 |
407 | }).call(this);
408 |
--------------------------------------------------------------------------------
/dist/leaflet-layerjson.src.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Leaflet JSON Layer v0.3.3 - 2019-12-27
3 | *
4 | * Copyright 2019 Stefano Cudini
5 | * stefano.cudini@gmail.com
6 | * https://opengeo.tech/
7 | *
8 | * Licensed under the MIT license.
9 | *
10 | * Demo:
11 | * https://opengeo.tech/maps/leaflet-layerjson/
12 | *
13 | * Source:
14 | * git@github.com:stefanocudini/leaflet-layerjson.git
15 | *
16 | */
17 |
18 | (function() {
19 |
20 | L.LayerJSON = L.FeatureGroup.extend({
21 |
22 | includes: L.version[0]==='1' ? L.Evented.prototype : L.Mixin.Events,
23 | //
24 | //Managed Events:
25 | // Event Data passed Description
26 | // dataloading {req: url|bbox} fired before ajax/jsonp request, req is bbox if url option is null
27 | // dataloaded {data: json} fired on ajax/jsonp request success
28 | //
29 | options: {
30 | url: '', //url map: "search.php?lat1={lat1}&lat2={lat2}&lon1={lon1}&lon2={lon2}"
31 | jsonpParam: null, //parameter name for jsonp requests
32 | callData: null, //custom function for data source, params: (req: url|bbox, callback: func), return {abort: func} or jQuery jqXHR Object
33 | filterData: null, //function that filter marker by its data, run before onEachMarker
34 | //
35 | locAsGeoJSON: false, //interpret location data as [lon, lat] value pair instead of [lat, lon]
36 | propertyItems: '', //json property used contains data items
37 | propertyTitle: 'title', //json property used as title(popup, marker, icon)
38 | propertyLoc: 'loc', //json property used as Latlng of marker use array for select double fields(ex. ['lat','lon'] )
39 | propertyId: 'id', //json property used to uniquely identify data items
40 | // // support dotted format: 'prop.subprop.title'
41 | layerTarget: null, //pre-existing layer to add markers, is a LayerGroup or L.MarkerClusterGroup https://goo.gl/tvmu0
42 | dataToMarker: null, //function that will be used for creating markers from json points, similar to pointToLayer of L.GeoJSON
43 | buildPopup: null, //function popup builder
44 | buildIcon: null, //function icon builder
45 | optsPopup: null, //popup options
46 | //
47 | minZoom: 10, //min zoom for call data
48 | caching: true, //enable requests caching
49 | cacheId: null, //function to generate id used to uniquely identify data items in cache
50 | minShift: 1000, //min shift before update data(in meters)
51 | precision: 6, //number of digit send to server for lat,lng precision
52 | updateMarkers: false, //update all markers in map to last results of callData
53 | attribution: '' //attribution text
54 | //TODO option: enabled, if false
55 | //TODO methods: enable()/disable()
56 | //TODO send map bounds decremented of certain margin
57 | },
58 |
59 | initialize: function(options) {
60 | L.FeatureGroup.prototype.initialize.call(this, []);
61 | L.Util.setOptions(this, options);
62 | this._dataToMarker = this.options.dataToMarker || this._defaultDataToMarker;
63 | this._buildIcon = this.options.buildIcon || this._defaultBuildIcon;
64 | this._filterData = this.options.filterData || null;
65 | this._hashUrl = this.options.url;
66 |
67 | if(this._hashUrl)
68 | {
69 | this._callData = this.getAjax;
70 | if(this.options.jsonpParam)
71 | {
72 | this._hashUrl += '&'+this.options.jsonpParam+'=';
73 | this._callData = this.getJsonp;
74 | }
75 | }
76 | else
77 | this._callData = this.options.callData;
78 |
79 | this._curReq = null;
80 | this._center = null;
81 | this._cacheBounds = null;
82 | this._markersCache = {}; //used for caching _dataToMarker builds
83 | },
84 |
85 | onAdd: function(map) {
86 |
87 | L.FeatureGroup.prototype.onAdd.call(this, map); //set this._map
88 | this._center = map.getCenter();
89 | this._cacheBounds = map.getBounds();
90 |
91 | map.on('moveend zoomend', this._onMove, this);
92 |
93 | this.update();
94 | },
95 |
96 | onRemove: function(map) {
97 |
98 | map.off('moveend zoomend', this._onMove, this);
99 |
100 | L.FeatureGroup.prototype.onRemove.call(this, map);
101 |
102 | for (var i in this._layers) {
103 | if (this._layers.hasOwnProperty(i)) {
104 | L.FeatureGroup.prototype.removeLayer.call(this, this._layers[i]);
105 | }
106 | }
107 | },
108 |
109 | getAttribution: function() {
110 | return this.options.attribution;
111 | },
112 |
113 | addLayer: function (layer) {
114 | if(this.options.layerTarget)
115 | {
116 | this.options.layerTarget.addLayer.call(this.options.layerTarget, layer);
117 | return this.fire('layeradd', {layer: layer});
118 | }
119 | else
120 | L.FeatureGroup.prototype.addLayer.call(this, layer);
121 | return this;
122 | },
123 |
124 | removeLayer: function (layer) {
125 | if(this.options.layerTarget)
126 | {
127 | this.options.layerTarget.removeLayer.call(this.options.layerTarget, layer);
128 | return this.fire('layerremove', {layer: layer});
129 | }
130 | else
131 | L.FeatureGroup.prototype.removeLayer.call(this, layer);
132 | return this;
133 | },
134 |
135 | clearLayers: function () {
136 |
137 | var self = this,
138 | newBounds = this._map.getBounds();
139 |
140 | //self._markersCache = {}; //cached gen markers
141 |
142 | if(self.options.layerTarget) {
143 | //self.options.layerTarget.clearLayers.call(self.options.layerTarget);
144 | self.options.layerTarget.eachLayer(function(l) {
145 | if(!self._contains(newBounds, l) ) {
146 | self.options.layerTarget.removeLayer(l);
147 | delete self._markersCache[l._cacheId];
148 | }
149 | });
150 | }
151 | else {
152 | //L.FeatureGroup.prototype.clearLayers.call(self);
153 | self.eachLayer(function(l) {
154 | if(!self._contains(newBounds, l) ) {
155 | self.removeLayer(l);
156 | delete self._markersCache[l._cacheId];
157 | }
158 | }, self);
159 | }
160 |
161 | return this;
162 | },
163 |
164 | _debouncer: function(func, timeout) {
165 | var timeoutID;
166 | timeout = timeout || 300;
167 | return function () {
168 | var scope = this , args = arguments;
169 | clearTimeout( timeoutID );
170 | timeoutID = setTimeout( function () {
171 | func.apply( scope , Array.prototype.slice.call( args ) );
172 | }, timeout);
173 | };
174 | },
175 |
176 | _getPath: function(obj, prop) {
177 | var parts = prop.split('.'),
178 | last = parts.pop(),
179 | len = parts.length,
180 | cur = parts[0],
181 | i = 1;
182 |
183 | if(len > 0)
184 | while((obj = obj[cur]) && i < len)
185 | cur = parts[i++];
186 |
187 | if(obj)
188 | return obj[last];
189 | },
190 |
191 | _defaultBuildIcon: function(data, title) {
192 | return new L.Icon.Default();
193 | },
194 |
195 | _defaultDataToMarker: function(data, latlng) { //make marker from data
196 |
197 | var title = this._getPath(data, this.options.propertyTitle),
198 | markerOpts = L.Util.extend({icon: this._buildIcon(data,title), title: title }, data),
199 | marker = new L.Marker(latlng, markerOpts ),
200 | htmlPopup = null;
201 |
202 | if(this.options.buildPopup && (htmlPopup = this.options.buildPopup(data, marker)))
203 | marker.bindPopup(htmlPopup, this.options.optsPopup );
204 |
205 | return marker;
206 | },
207 |
208 | addMarker: function(data) {
209 |
210 | var loc, hash, propLoc = this.options.propertyLoc;
211 |
212 | if( L.Util.isArray(propLoc) ) {
213 | loc = L.latLng( parseFloat( this._getPath(data, propLoc[0]) ),
214 | parseFloat( this._getPath(data, propLoc[1]) ) );
215 | }
216 | else {
217 | if (this.options.locAsGeoJSON) {
218 | var lnglat = this._getPath(data, propLoc);
219 | loc = L.latLng( lnglat[1], lnglat[0] );
220 | } else {
221 | loc = L.latLng( this._getPath(data, propLoc) );
222 | }
223 | }
224 |
225 | if(this.options.cacheId) {
226 | hash = this.options.cacheId.call(this, data, loc);
227 | }
228 | else {
229 | if(this.options.propertyId)
230 | hash = this._getPath(data, this.options.propertyId);
231 | else
232 | hash = loc.lat+''+loc.lng+''+this._getPath(data, this.options.propertyTitle);
233 | }
234 |
235 | if(typeof this._markersCache[hash] === 'undefined') {
236 | this._markersCache[hash] = this._dataToMarker(data, loc);
237 | this._markersCache[hash]._cacheId = hash;
238 | this.addLayer( this._markersCache[hash] );
239 | }
240 |
241 | return hash;
242 | },
243 |
244 | _contains: function(bounds, el) {
245 | var loc;
246 |
247 | if(el.getLatLng)
248 | loc = el.getLatLng();
249 | else if(el.getBounds)
250 | loc = el.getBounds();
251 |
252 | return bounds.contains(loc);
253 | },
254 |
255 | _loadCacheToBounds: function(bounds) { //show/hide cached markers
256 | for(var i in this._markersCache)
257 | {
258 | if(this._markersCache[i])
259 | {
260 | if(this._contains(bounds, this._markersCache[i]) )
261 | this.addLayer(this._markersCache[i]);
262 | else
263 | this.removeLayer(this._markersCache[i]);
264 | }
265 | }
266 | },
267 |
268 | _onMove: function(e) {
269 | var newZoom = this._map.getZoom(),
270 | newCenter = this._map.getCenter(),
271 | newBounds = this._map.getBounds();
272 |
273 | if(newZoom < this.options.minZoom)
274 | return false;
275 |
276 | if( this.options.minShift && this._center.distanceTo(newCenter) < this.options.minShift )
277 | return false;
278 | else
279 | this._center = newCenter;
280 |
281 | if(this.options.caching) {
282 |
283 | if( this._cacheBounds.contains(newBounds) )
284 | {
285 | this._loadCacheToBounds(newBounds);
286 | return false;
287 | }
288 | else
289 | this._cacheBounds.extend(newBounds);
290 | }
291 | else
292 | this.clearLayers();
293 |
294 | this.update();
295 | },
296 |
297 | update: function() { //populate target layer
298 |
299 | var self = this;
300 |
301 | var prec = self.options.precision,
302 | bb = self._map.getBounds(),
303 | sw = bb.getSouthWest(),
304 | ne = bb.getNorthEast(),
305 | bbox = [
306 | [ parseFloat(sw.lat.toFixed(prec)), parseFloat(sw.lng.toFixed(prec)) ],
307 | [ parseFloat(ne.lat.toFixed(prec)), parseFloat(ne.lng.toFixed(prec)) ]
308 | ];
309 |
310 | if(self._hashUrl) //conver bbox to url string
311 | bbox = L.Util.template(self._hashUrl, {
312 | lat1: bbox[0][0], lon1: bbox[0][1],
313 | lat2: bbox[1][0], lon2: bbox[1][1]
314 | });
315 |
316 | if(self._curReq && self._curReq.abort)
317 | self._curReq.abort(); //prevent parallel requests
318 |
319 |
320 | self.fire('dataloading', {req: bbox });
321 | self._curReq = self._callData(bbox, function(json) {
322 |
323 | //console.log('callData',json)
324 |
325 | self._curReq = null;
326 |
327 | if(self._filterData)
328 | json = self._filterData(json);
329 |
330 | if(self.options.propertyItems)
331 | json = self._getPath(json, self.options.propertyItems);
332 |
333 | self.fire('dataloaded', {data: json});
334 |
335 | self.updateMarkers(json);
336 | });
337 | },
338 |
339 | updateMarkers: function(json) {
340 | var jsonbyhash = {};
341 |
342 | for (var k in json) {
343 | if (!isNaN(parseFloat(k)) && isFinite(k)) {
344 | var h = this.addMarker.call(this, json[k]);
345 | jsonbyhash[h] = 1;
346 | }
347 | }
348 |
349 | if(this.options.updateMarkers) {
350 | for (var c in this._markersCache) {
351 | if(!jsonbyhash[ this._markersCache[c]._cacheId ])
352 | this.removeLayer(this._markersCache[c]);
353 | else
354 | this.addLayer(this._markersCache[c]);
355 | }
356 | }
357 | },
358 |
359 |
360 | /////////////////ajax jsonp methods
361 |
362 | getAjax: function(url, cb) { //default ajax request
363 |
364 | if (window.XMLHttpRequest === undefined) {
365 | window.XMLHttpRequest = function() {
366 | try {
367 | return new ActiveXObject("Microsoft.XMLHTTP.6.0");
368 | }
369 | catch (e1) {
370 | try {
371 | return new ActiveXObject("Microsoft.XMLHTTP.3.0");
372 | }
373 | catch (e2) {
374 | throw new Error("XMLHttpRequest is not supported");
375 | }
376 | }
377 | };
378 | }
379 | var request = new XMLHttpRequest();
380 | request.open('GET', url);
381 | request.onreadystatechange = function() {
382 | var response = {};
383 | if (request.readyState === 4 && request.status === 200) {
384 | try {
385 | if(window.JSON)
386 | response = JSON.parse(request.responseText);
387 | else
388 | response = eval("("+ request.responseText + ")");
389 | } catch(err) {
390 | response = {};
391 | throw new Error('Ajax response is not JSON');
392 | }
393 | cb(response);
394 | }
395 | };
396 | request.send();
397 | return request;
398 | },
399 |
400 | getJsonp: function(url, cb) { //extract searched records from remote jsonp service
401 | var body = document.getElementsByTagName('body')[0],
402 | script = L.DomUtil.create('script','leaflet-layerjson-jsonp', body );
403 |
404 | //JSONP callback
405 | L.LayerJSON.callJsonp = function(data) {
406 | cb(data);
407 | body.removeChild(script);
408 | }
409 | script.type = 'text/javascript';
410 | script.src = url+'L.LayerJSON.callJsonp';
411 | return {
412 | abort: function() {
413 | script.parentNode.removeChild(script);
414 | }
415 | };
416 | }
417 | });
418 |
419 | L.layerJSON = function (options) {
420 | return new L.LayerJSON(options);
421 | };
422 |
423 | }).call(this);
424 |
--------------------------------------------------------------------------------