This repository was archived by the owner on Jul 17, 2024. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathExt.ux.touch.MapLoader.js
268 lines (227 loc) · 8.94 KB
/
Ext.ux.touch.MapLoader.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
/**
* Ext.ux.touch.MapLoader.js
*
* @author SwarmOnline.com (Stuart Ashworth & Andrew Duncan)
* @copyright (c) 2011, by SwarmOnline.com
* @date 9th May 2011
* @version 1.1
* @documentation http://www.swarmonline.com/2011/01/ext-ux-touch-maploader-dynamically-load-map-points-as-you-pan-around-a-map
* @website http://www.swarmonline.com
*
* @license Ext.ux.touch.MapLoader.js is licensed under the terms of the Open Source
* LGPL 3.0 license. Commercial use is permitted to the extent that the
* code/component(s) do NOT become part of another Open Source or Commercially
* licensed development library or toolkit without explicit permission.
*
* License details: http://www.gnu.org/licenses/lgpl.html
*/
Ext.ns('Ext.ux.touch');
Ext.ux.touch.MapLoader = Ext.extend(Ext.util.Observable, {
/**
* Decides which units (Miles or Kilometers) are used for radiuses and other distances
* Possible values are 'miles' and 'km'
*/
units: 'miles', // 'miles' or 'km',
/**
* A buffer is used to load markers just outside the visible area so markers are visible
* straight after panning to make the loading appear seamless
* Possible Values:
* fixed: a fixed value of miles or kilometres (see 'units' config)
* ratio: a ratio (between 0 and 1) of the maps bounding circle's radius so that it is proportional to the
* zoom level
*/
bufferType: 'ratio', // ratio or fixed
/**
* The value relating to the 'bufferType' config
* Either a decimal between 0 and 1 for ratio types or any number for fixed bufferType
*/
buffer: 0.05,
/**
* Set this to a Store object and the plugin will automatically reload it passing up
* the necessary coordinates and distances
*/
store: null,
/**
* If set to true stops the plugin from doing loads or firing the 'mapload' event
*/
disabled: false,
/**
* Time in milliseconds between extra loads WHILE the user is dragging the map.
* Useful for larger screened devices (tablets) where the distance the user can
* pan is greater so points might be available before the user stops dragging
* 0 = no interval loads
*/
loadInterval: 0,
lastIntervalLoadDate: new Date(),
init: function(parent){
// cache the reference to parent for later
this.parent = parent;
// Add the 'mapload' event for consumer to use to load the new data
this.parent.addEvents('mapload');
// Setup the listeners
this.parent.on({
maprender: this.onMapRender,
destroy: this.onDestroy,
scope: this
});
// If the store is configured then add our own handler to the 'mapload' event
if (!Ext.isEmpty(this.store)) {
this.parent.on({
mapload: this.onMapLoad,
scope: this
});
}
},
/**
* Clean up listeners etc after destroy
*/
onDestroy: function(){
this.parent.removeListener('maprender', this.onMapRender);
if (!Ext.isEmpty(this.store)) {
this.parent.removeListener('mapload', this.onMapLoad);
}
},
/**
* Executed after the map has rendered
* This sets up the listeners that can't be added until the map has been rendered
*/
onMapRender: function(){
google.maps.event.addListener(this.parent.map, 'moveend', Ext.createDelegate(this.onMoveEnd, this));
google.maps.event.addListener(this.parent.map, 'bounds_changed', Ext.createDelegate(this.onBoundsChanged, this));
},
onMapLoad: function(centre, bounds, boundingRadius, bufferRadius, zoom){
this.store.load({
params: {
centre: Ext.encode(centre),
bounds: Ext.encode(bounds),
boundingRadius: boundingRadius,
bufferRadius: bufferRadius,
zoom: zoom
}
});
},
/**
* Executed when the bounds have changed, which includes panning and zooming
* Starts the simulating of the 'moveend' event
* @param {Object} e
*/
onBoundsChanged: function(e){
if (!this.disabled) {
var currentTime = new Date();
// if time between Current Time and Last Interval Load Time is greater than
// 'loadInterval' option, only if 'loadInterval' is greater than 0
if ((currentTime.getTime() - this.lastIntervalLoadDate.getTime()) >= this.loadInterval && this.loadInterval > 0) {
this.onMoveEnd(this.parent.map.getCenter()); // do the load
this.lastIntervalLoadDate = new Date(); // update the lastIntervalLoadDate property
}
this.createMoveEndEvent(this.parent.map.getCenter());
}
},
/**
* Simulates a 'MoveEnd' event which is fired when the map has stopped being panned or zoomed
* Starts a timer which will execute once the bounds_changed events have stopped
* If they continue the previously created timer is destroyed and a new one started.
* @param {Object} centre the new Centre of the map after the bounds have changed
*/
createMoveEndEvent: function(centre){
clearTimeout(this.timeout);
this.timeout = setTimeout(Ext.createDelegate(this.raiseMoveEndEvent, this, [centre]), 100);
},
raiseMoveEndEvent: function(centre){
google.maps.event.trigger(this.parent.map, 'moveend', centre);
},
/**
* Handler to process the MoveEnd event and raise the plugins own 'mapload' event
* which is used to load the new locations
* @param {Object} centre
*/
onMoveEnd: function(centre){
var bounds = this.parent.map.getBounds(); // the Map's Bounding Box
// Make the positions a little more user friendly!
var centreNorm = {
lat: centre.lat(),
lng: centre.lng()
};
var boundsNorm = {
northeast: {
lat: bounds.getNorthEast().lat(),
lng: bounds.getNorthEast().lng()
},
southwest: {
lat: bounds.getSouthWest().lat(),
lng: bounds.getSouthWest().lng()
}
};
var boundingRadius = this.getBoundingRadius(centreNorm, boundsNorm); // the radius of the map's bounding circle
var bufferRadius = this.getBufferRadius(boundingRadius); // the amount of extra distance to add to help smoother loading
var zoom = this.parent.map.getZoom(); // the current zoom setting of the map
// Fire the 'mapload' event so the consuming code can load the new data
this.parent.fireEvent('mapload', centreNorm, boundsNorm, boundingRadius, bufferRadius, zoom);
},
/**
* Returns radius (in miles or KM depending on the value or the 'units' config) of the
* maps visible bounding circle
* Sent to server to aid with calculating how much to bring back
* @param {Object} centre
* @param {Object} bounds
*/
getBoundingRadius: function(centre, bounds){
// return the radius (distance between the maps centre and one of its corners
// this will provide the radius of a bounding circle that encompasses the entire visible map
return this.getDistanceBetweenPoints(centre, bounds.northeast, this.units);
},
/**
* Gets the number of miles or km extra that should be loaded
* on top of the visible radius so loading appears smoother
* @param {Object} boundingRadius
*/
getBufferRadius: function(boundingRadius){
var bufferRadius = this.buffer;
if (this.bufferType === 'ratio') {
bufferRadius = boundingRadius * this.buffer;
}
return bufferRadius;
},
/**
* Uses the Haversine Formula to calculate the distance between two lat/lng coordinates
* Pass values in the format {lat xx, lng yy}
* @param {Object} pos1
* @param {Object} pos2
*/
getDistanceBetweenPoints: function(pos1, pos2, units){
var earthRadius = {
miles: 3958.8,
km: 6371
};
var R = earthRadius[units || 'miles'];
var lat1 = pos1.lat;
var lon1 = pos1.lng;
var lat2 = pos2.lat;
var lon2 = pos2.lng;
var dLat = this.toRad((lat2 - lat1));
var dLon = this.toRad((lon2 - lon1));
var a = Math.sin(dLat / 2) * Math.sin(dLat / 2) +
Math.cos(this.toRad(lat1)) * Math.cos(this.toRad(lat2)) *
Math.sin(dLon / 2) *
Math.sin(dLon / 2);
var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
var d = R * c;
return d;
},
/**
* Helper function to convert Degrees to Radians
* @param {Object} x
*/
toRad: function(x){
return x * Math.PI / 180;
},
enable: function(){
this.disabled = false;
},
disable: function(){
this.disabled = true;
},
setDisabled: function(disabled){
this.disabled = disabled;
}
});