refinerycms-map 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,70 @@
1
+ HTTP_ERRORS = [ Timeout::Error,
2
+ Errno::EINVAL,
3
+ Errno::ECONNRESET,
4
+ EOFError,
5
+ Net::HTTPBadResponse,
6
+ Net::HTTPHeaderSyntaxError,
7
+ Net::ProtocolError ] unless defined?(HTTP_ERRORS)
8
+
9
+ module Geocode
10
+
11
+ Coordinate = Struct.new(:latitude, :longitude)
12
+
13
+ class Response
14
+
15
+ def initialize(http_response)
16
+ require 'json/pure' unless defined?(JSON::JSON_LOADED)
17
+ @response = http_response
18
+ end
19
+
20
+ def data
21
+ @data ||= JSON.parse(@response.body)
22
+ end
23
+
24
+ def coordinate
25
+ @coordinate ||= begin
26
+ coordinate = data['results'][0]['geometry']['location']
27
+ Geocode::Coordinate.new(coordinate['lat'], coordinate['lng'])
28
+ end
29
+ end
30
+
31
+ def success?
32
+ @response.kind_of?(Net::HTTPSuccess) && data['status'] == 'OK'
33
+ rescue
34
+ false
35
+ end
36
+
37
+ end
38
+
39
+
40
+ def self.find_address(address)
41
+ query_for_coordinates(address) || default_coordinate
42
+ end
43
+
44
+
45
+ private
46
+
47
+
48
+ def self.query_for_coordinates(address)
49
+ response = request(address)
50
+ response.coordinate if response.success?
51
+ rescue *HTTP_ERRORS
52
+ end
53
+
54
+ def self.default_coordinate
55
+ Coordinate.new(0,0)
56
+ end
57
+
58
+ def self.request(address)
59
+ require 'net/http'
60
+ require 'cgi'
61
+ require 'uri'
62
+
63
+ uri = URI.parse("http://maps.google.com/maps/api/geocode/json?address=#{CGI.escape(address)}&sensor=false")
64
+ Net::HTTP.start(uri.host, uri.port) do |http|
65
+ Geocode::Response.new(http.get("#{uri.path}?#{uri.query}"))
66
+ end
67
+ end
68
+
69
+ end
70
+
@@ -0,0 +1,20 @@
1
+ <div id="map-side-bar">
2
+ <% if @map_locations.empty? %>
3
+ <p>No locations found near <strong><%= params[:q] %></strong></p>
4
+ <% else %>
5
+ <% @map_locations.each do |loc| %>
6
+ <div class="map-location" data-jmapping="{id: <%= loc.id %>, point: {lat: <%= loc.lat %>, lng: <%= loc.lng %>}}">
7
+ <a href="#" class="map-link"><%= loc.title %></a>
8
+ <div class="info-box">
9
+ <p><%= loc.title %></p>
10
+ <p>
11
+ <%= loc.address %><br/>
12
+ <%= loc.city %>, <%= loc.state %> <%= loc.zipcode %>
13
+ </p>
14
+ <p><%= loc.phone %></p>
15
+ <p><%= loc.url %></p>
16
+ </div>
17
+ </div>
18
+ <% end %>
19
+ <% end %>
20
+ </div>
@@ -0,0 +1,14 @@
1
+ <div id="googlemap" style="width:<%= width %>px; height:<%= height %>px;"></div>
2
+
3
+ <%= javascript_include_tag "http://ajax.googleapis.com/ajax/libs/jquery/1.3.2/jquery.min.js" %>
4
+ <%= javascript_include_tag "http://maps.google.com/maps?file=api&v=2&key=ABQIAAAANPKSmZLGixaiXPm4axsrNhRL7aHN84IgYddYDMrnQQV95WOVBRSBz4pFe5Nc4ZzbzY0MAvmogBnSJA" %>
5
+ <%= javascript_include_tag "mapiconmaker.js" %>
6
+ <%= javascript_include_tag "markermanager.js" %>
7
+ <%= javascript_include_tag "jquery.metadata.js" %>
8
+ <%= javascript_include_tag "jquery.jmapping.min.js" %>
9
+
10
+ <script type="text/javascript">
11
+ $(document).ready(function(){
12
+ $('#googlemap').jMapping();
13
+ });
14
+ </script>
@@ -0,0 +1,5 @@
1
+ <% form_tag map_locations_url, :method => :get do %>
2
+ <%= label_tag "Enter address, city, or zip code" %><br/>
3
+ <%= text_field_tag :q, params[:q] %><br/>
4
+ <%= submit_tag "Find nearby locations" %>
5
+ <% end %>
@@ -0,0 +1,815 @@
1
+ /**
2
+ * @name MarkerManager
3
+ * @version 1.1
4
+ * @copyright (c) 2007 Google Inc.
5
+ * @author Doug Ricket, others
6
+ *
7
+ * @fileoverview Marker manager is an interface between the map and the user,
8
+ * designed to manage adding and removing many points when the viewport changes.
9
+ * <br /><br />
10
+ * <b>How it Works</b>:<br/>
11
+ * The MarkerManager places its markers onto a grid, similar to the map tiles.
12
+ * When the user moves the viewport, it computes which grid cells have
13
+ * entered or left the viewport, and shows or hides all the markers in those
14
+ * cells.
15
+ * (If the users scrolls the viewport beyond the markers that are loaded,
16
+ * no markers will be visible until the <code>EVENT_moveend</code>
17
+ * triggers an update.)
18
+ * In practical consequences, this allows 10,000 markers to be distributed over
19
+ * a large area, and as long as only 100-200 are visible in any given viewport,
20
+ * the user will see good performance corresponding to the 100 visible markers,
21
+ * rather than poor performance corresponding to the total 10,000 markers.
22
+ * Note that some code is optimized for speed over space,
23
+ * with the goal of accommodating thousands of markers.
24
+ */
25
+
26
+ /*
27
+ * Licensed under the Apache License, Version 2.0 (the "License");
28
+ * you may not use this file except in compliance with the License.
29
+ * You may obtain a copy of the License at
30
+ *
31
+ * http://www.apache.org/licenses/LICENSE-2.0
32
+ *
33
+ * Unless required by applicable law or agreed to in writing, software
34
+ * distributed under the License is distributed on an "AS IS" BASIS,
35
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
36
+ * See the License for the specific language governing permissions and
37
+ * limitations under the License.
38
+ */
39
+
40
+ /**
41
+ * @name MarkerManagerOptions
42
+ * @class This class represents optional arguments to the {@link MarkerManager}
43
+ * constructor.
44
+ * @property {Number} [maxZoom] Sets the maximum zoom level monitored by a
45
+ * marker manager. If not given, the manager assumes the maximum map zoom
46
+ * level. This value is also used when markers are added to the manager
47
+ * without the optional {@link maxZoom} parameter.
48
+ * @property {Number} [borderPadding] Specifies, in pixels, the extra padding
49
+ * outside the map's current viewport monitored by a manager. Markers that
50
+ * fall within this padding are added to the map, even if they are not fully
51
+ * visible.
52
+ * @property {Boolean} [trackMarkers=false] Indicates whether or not a marker
53
+ * manager should track markers' movements. If you wish to move managed
54
+ * markers using the {@link setPoint}/{@link setLatLng} methods,
55
+ * this option should be set to {@link true}.
56
+ */
57
+
58
+ /**
59
+ * Creates a new MarkerManager that will show/hide markers on a map.
60
+ *
61
+ * @constructor
62
+ * @param {Map} map The map to manage.
63
+ * @param {Object} opt_opts A container for optional arguments:
64
+ * {Number} maxZoom The maximum zoom level for which to create tiles.
65
+ * {Number} borderPadding The width in pixels beyond the map border,
66
+ * where markers should be display.
67
+ * {Boolean} trackMarkers Whether or not this manager should track marker
68
+ * movements.
69
+ */
70
+ function MarkerManager(map, opt_opts) {
71
+ var me = this;
72
+ me.map_ = map;
73
+ me.mapZoom_ = map.getZoom();
74
+ me.projection_ = map.getCurrentMapType().getProjection();
75
+
76
+ opt_opts = opt_opts || {};
77
+ me.tileSize_ = MarkerManager.DEFAULT_TILE_SIZE_;
78
+
79
+ var mapTypes = map.getMapTypes();
80
+ var mapMaxZoom = mapTypes[0].getMaximumResolution();
81
+ for (var i = 0; i < mapTypes.length; i++) {
82
+ var mapTypeMaxZoom = mapTypes[i].getMaximumResolution();
83
+ if (mapTypeMaxZoom > mapMaxZoom) {
84
+ mapMaxZoom = mapTypeMaxZoom;
85
+ }
86
+ }
87
+ me.maxZoom_ = opt_opts.maxZoom || mapMaxZoom;
88
+
89
+ me.trackMarkers_ = opt_opts.trackMarkers;
90
+ me.show_ = opt_opts.show || true;
91
+
92
+ var padding;
93
+ if (typeof opt_opts.borderPadding === "number") {
94
+ padding = opt_opts.borderPadding;
95
+ } else {
96
+ padding = MarkerManager.DEFAULT_BORDER_PADDING_;
97
+ }
98
+ // The padding in pixels beyond the viewport, where we will pre-load markers.
99
+ me.swPadding_ = new GSize(-padding, padding);
100
+ me.nePadding_ = new GSize(padding, -padding);
101
+ me.borderPadding_ = padding;
102
+
103
+ me.gridWidth_ = [];
104
+
105
+ me.grid_ = [];
106
+ me.grid_[me.maxZoom_] = [];
107
+ me.numMarkers_ = [];
108
+ me.numMarkers_[me.maxZoom_] = 0;
109
+
110
+ GEvent.bind(map, "moveend", me, me.onMapMoveEnd_);
111
+
112
+ // NOTE: These two closures provide easy access to the map.
113
+ // They are used as callbacks, not as methods.
114
+ me.removeOverlay_ = function (marker) {
115
+ map.removeOverlay(marker);
116
+ me.shownMarkers_--;
117
+ };
118
+ me.addOverlay_ = function (marker) {
119
+ if (me.show_) {
120
+ map.addOverlay(marker);
121
+ me.shownMarkers_++;
122
+ }
123
+ };
124
+
125
+ me.resetManager_();
126
+ me.shownMarkers_ = 0;
127
+
128
+ me.shownBounds_ = me.getMapGridBounds_();
129
+ }
130
+
131
+ // Static constants:
132
+ MarkerManager.DEFAULT_TILE_SIZE_ = 1024;
133
+ MarkerManager.DEFAULT_BORDER_PADDING_ = 100;
134
+ MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE = 256;
135
+
136
+
137
+ /**
138
+ * Initializes MarkerManager arrays for all zoom levels
139
+ * Called by constructor and by clearAllMarkers
140
+ */
141
+ MarkerManager.prototype.resetManager_ = function () {
142
+ var me = this;
143
+ var mapWidth = MarkerManager.MERCATOR_ZOOM_LEVEL_ZERO_RANGE;
144
+ for (var zoom = 0; zoom <= me.maxZoom_; ++zoom) {
145
+ me.grid_[zoom] = [];
146
+ me.numMarkers_[zoom] = 0;
147
+ me.gridWidth_[zoom] = Math.ceil(mapWidth / me.tileSize_);
148
+ mapWidth <<= 1;
149
+ }
150
+ };
151
+
152
+ /**
153
+ * Removes all markers in the manager, and
154
+ * removes any visible markers from the map.
155
+ */
156
+ MarkerManager.prototype.clearMarkers = function () {
157
+ var me = this;
158
+ me.processAll_(me.shownBounds_, me.removeOverlay_);
159
+ me.resetManager_();
160
+ };
161
+
162
+
163
+ /**
164
+ * Gets the tile coordinate for a given latlng point.
165
+ *
166
+ * @param {LatLng} latlng The geographical point.
167
+ * @param {Number} zoom The zoom level.
168
+ * @param {GSize} padding The padding used to shift the pixel coordinate.
169
+ * Used for expanding a bounds to include an extra padding
170
+ * of pixels surrounding the bounds.
171
+ * @return {GPoint} The point in tile coordinates.
172
+ *
173
+ */
174
+ MarkerManager.prototype.getTilePoint_ = function (latlng, zoom, padding) {
175
+ var pixelPoint = this.projection_.fromLatLngToPixel(latlng, zoom);
176
+ return new GPoint(
177
+ Math.floor((pixelPoint.x + padding.width) / this.tileSize_),
178
+ Math.floor((pixelPoint.y + padding.height) / this.tileSize_));
179
+ };
180
+
181
+
182
+ /**
183
+ * Finds the appropriate place to add the marker to the grid.
184
+ * Optimized for speed; does not actually add the marker to the map.
185
+ * Designed for batch-processing thousands of markers.
186
+ *
187
+ * @param {Marker} marker The marker to add.
188
+ * @param {Number} minZoom The minimum zoom for displaying the marker.
189
+ * @param {Number} maxZoom The maximum zoom for displaying the marker.
190
+ */
191
+ MarkerManager.prototype.addMarkerBatch_ = function (marker, minZoom, maxZoom) {
192
+ var mPoint = marker.getPoint();
193
+ marker.MarkerManager_minZoom = minZoom;
194
+ // Tracking markers is expensive, so we do this only if the
195
+ // user explicitly requested it when creating marker manager.
196
+ if (this.trackMarkers_) {
197
+ GEvent.bind(marker, "changed", this, this.onMarkerMoved_);
198
+ }
199
+
200
+ var gridPoint = this.getTilePoint_(mPoint, maxZoom, GSize.ZERO);
201
+
202
+ for (var zoom = maxZoom; zoom >= minZoom; zoom--) {
203
+ var cell = this.getGridCellCreate_(gridPoint.x, gridPoint.y, zoom);
204
+ cell.push(marker);
205
+
206
+ gridPoint.x = gridPoint.x >> 1;
207
+ gridPoint.y = gridPoint.y >> 1;
208
+ }
209
+ };
210
+
211
+
212
+ /**
213
+ * Returns whether or not the given point is visible in the shown bounds. This
214
+ * is a helper method that takes care of the corner case, when shownBounds have
215
+ * negative minX value.
216
+ *
217
+ * @param {Point} point a point on a grid.
218
+ * @return {Boolean} Whether or not the given point is visible in the currently
219
+ * shown bounds.
220
+ */
221
+ MarkerManager.prototype.isGridPointVisible_ = function (point) {
222
+ var me = this;
223
+ var vertical = me.shownBounds_.minY <= point.y &&
224
+ point.y <= me.shownBounds_.maxY;
225
+ var minX = me.shownBounds_.minX;
226
+ var horizontal = minX <= point.x && point.x <= me.shownBounds_.maxX;
227
+ if (!horizontal && minX < 0) {
228
+ // Shifts the negative part of the rectangle. As point.x is always less
229
+ // than grid width, only test shifted minX .. 0 part of the shown bounds.
230
+ var width = me.gridWidth_[me.shownBounds_.z];
231
+ horizontal = minX + width <= point.x && point.x <= width - 1;
232
+ }
233
+ return vertical && horizontal;
234
+ };
235
+
236
+
237
+ /**
238
+ * Reacts to a notification from a marker that it has moved to a new location.
239
+ * It scans the grid all all zoom levels and moves the marker from the old grid
240
+ * location to a new grid location.
241
+ *
242
+ * @param {Marker} marker The marker that moved.
243
+ * @param {LatLng} oldPoint The old position of the marker.
244
+ * @param {LatLng} newPoint The new position of the marker.
245
+ */
246
+ MarkerManager.prototype.onMarkerMoved_ = function (marker, oldPoint, newPoint) {
247
+ // NOTE: We do not know the minimum or maximum zoom the marker was
248
+ // added at, so we start at the absolute maximum. Whenever we successfully
249
+ // remove a marker at a given zoom, we add it at the new grid coordinates.
250
+ var me = this;
251
+ var zoom = me.maxZoom_;
252
+ var changed = false;
253
+ var oldGrid = me.getTilePoint_(oldPoint, zoom, GSize.ZERO);
254
+ var newGrid = me.getTilePoint_(newPoint, zoom, GSize.ZERO);
255
+ while (zoom >= 0 && (oldGrid.x !== newGrid.x || oldGrid.y !== newGrid.y)) {
256
+ var cell = me.getGridCellNoCreate_(oldGrid.x, oldGrid.y, zoom);
257
+ if (cell) {
258
+ if (me.removeFromArray_(cell, marker)) {
259
+ me.getGridCellCreate_(newGrid.x, newGrid.y, zoom).push(marker);
260
+ }
261
+ }
262
+ // For the current zoom we also need to update the map. Markers that no
263
+ // longer are visible are removed from the map. Markers that moved into
264
+ // the shown bounds are added to the map. This also lets us keep the count
265
+ // of visible markers up to date.
266
+ if (zoom === me.mapZoom_) {
267
+ if (me.isGridPointVisible_(oldGrid)) {
268
+ if (!me.isGridPointVisible_(newGrid)) {
269
+ me.removeOverlay_(marker);
270
+ changed = true;
271
+ }
272
+ } else {
273
+ if (me.isGridPointVisible_(newGrid)) {
274
+ me.addOverlay_(marker);
275
+ changed = true;
276
+ }
277
+ }
278
+ }
279
+ oldGrid.x = oldGrid.x >> 1;
280
+ oldGrid.y = oldGrid.y >> 1;
281
+ newGrid.x = newGrid.x >> 1;
282
+ newGrid.y = newGrid.y >> 1;
283
+ --zoom;
284
+ }
285
+ if (changed) {
286
+ me.notifyListeners_();
287
+ }
288
+ };
289
+
290
+
291
+ /**
292
+ * Removes marker from the manager and from the map
293
+ * (if it's currently visible).
294
+ * @param {GMarker} marker The marker to delete.
295
+ */
296
+ MarkerManager.prototype.removeMarker = function (marker) {
297
+ var me = this;
298
+ var zoom = me.maxZoom_;
299
+ var changed = false;
300
+ var point = marker.getPoint();
301
+ var grid = me.getTilePoint_(point, zoom, GSize.ZERO);
302
+ while (zoom >= 0) {
303
+ var cell = me.getGridCellNoCreate_(grid.x, grid.y, zoom);
304
+
305
+ if (cell) {
306
+ me.removeFromArray_(cell, marker);
307
+ }
308
+ // For the current zoom we also need to update the map. Markers that no
309
+ // longer are visible are removed from the map. This also lets us keep the count
310
+ // of visible markers up to date.
311
+ if (zoom === me.mapZoom_) {
312
+ if (me.isGridPointVisible_(grid)) {
313
+ me.removeOverlay_(marker);
314
+ changed = true;
315
+ }
316
+ }
317
+ grid.x = grid.x >> 1;
318
+ grid.y = grid.y >> 1;
319
+ --zoom;
320
+ }
321
+ if (changed) {
322
+ me.notifyListeners_();
323
+ }
324
+ me.numMarkers_[marker.MarkerManager_minZoom]--;
325
+ };
326
+
327
+
328
+ /**
329
+ * Add many markers at once.
330
+ * Does not actually update the map, just the internal grid.
331
+ *
332
+ * @param {Array of Marker} markers The markers to add.
333
+ * @param {Number} minZoom The minimum zoom level to display the markers.
334
+ * @param {Number} opt_maxZoom The maximum zoom level to display the markers.
335
+ */
336
+ MarkerManager.prototype.addMarkers = function (markers, minZoom, opt_maxZoom) {
337
+ var maxZoom = this.getOptMaxZoom_(opt_maxZoom);
338
+ for (var i = markers.length - 1; i >= 0; i--) {
339
+ this.addMarkerBatch_(markers[i], minZoom, maxZoom);
340
+ }
341
+
342
+ this.numMarkers_[minZoom] += markers.length;
343
+ };
344
+
345
+
346
+ /**
347
+ * Returns the value of the optional maximum zoom. This method is defined so
348
+ * that we have just one place where optional maximum zoom is calculated.
349
+ *
350
+ * @param {Number} opt_maxZoom The optinal maximum zoom.
351
+ * @return The maximum zoom.
352
+ */
353
+ MarkerManager.prototype.getOptMaxZoom_ = function (opt_maxZoom) {
354
+ return opt_maxZoom || this.maxZoom_;
355
+ };
356
+
357
+
358
+ /**
359
+ * Calculates the total number of markers potentially visible at a given
360
+ * zoom level.
361
+ *
362
+ * @param {Number} zoom The zoom level to check.
363
+ */
364
+ MarkerManager.prototype.getMarkerCount = function (zoom) {
365
+ var total = 0;
366
+ for (var z = 0; z <= zoom; z++) {
367
+ total += this.numMarkers_[z];
368
+ }
369
+ return total;
370
+ };
371
+
372
+ /**
373
+ * Returns a marker given latitude, longitude and zoom. If the marker does not
374
+ * exist, the method will return a new marker. If a new marker is created,
375
+ * it will NOT be added to the manager.
376
+ *
377
+ * @param {Number} lat - the latitude of a marker.
378
+ * @param {Number} lng - the longitude of a marker.
379
+ * @param {Number} zoom - the zoom level
380
+ * @return {GMarker} marker - the marker found at lat and lng
381
+ */
382
+ MarkerManager.prototype.getMarker = function(lat, lng, zoom) {
383
+ var me = this;
384
+ var mPoint = new GLatLng(lat, lng);
385
+ var gridPoint = me.getTilePoint_(mPoint, zoom, GSize.ZERO);
386
+
387
+ var marker = new GMarker(mPoint);
388
+ var cellArray = me.getGridCellNoCreate_(gridPoint.x, gridPoint.y, zoom);
389
+ if(cellArray != undefined){
390
+ for (var i = 0; i < cellArray.length; i++)
391
+ {
392
+ if(lat == cellArray[i].getLatLng().lat() &&
393
+ lng == cellArray[i].getLatLng().lng())
394
+ {
395
+ marker = cellArray[i];
396
+ }
397
+ }
398
+ }
399
+ return marker;
400
+ };
401
+
402
+ /**
403
+ * Add a single marker to the map.
404
+ *
405
+ * @param {Marker} marker The marker to add.
406
+ * @param {Number} minZoom The minimum zoom level to display the marker.
407
+ * @param {Number} opt_maxZoom The maximum zoom level to display the marker.
408
+ */
409
+ MarkerManager.prototype.addMarker = function (marker, minZoom, opt_maxZoom) {
410
+ var me = this;
411
+ var maxZoom = this.getOptMaxZoom_(opt_maxZoom);
412
+ me.addMarkerBatch_(marker, minZoom, maxZoom);
413
+ var gridPoint = me.getTilePoint_(marker.getPoint(), me.mapZoom_, GSize.ZERO);
414
+ if (me.isGridPointVisible_(gridPoint) &&
415
+ minZoom <= me.shownBounds_.z &&
416
+ me.shownBounds_.z <= maxZoom) {
417
+ me.addOverlay_(marker);
418
+ me.notifyListeners_();
419
+ }
420
+ this.numMarkers_[minZoom]++;
421
+ };
422
+
423
+ /**
424
+ * Returns true if this bounds (inclusively) contains the given point.
425
+ * @param {Point} point The point to test.
426
+ * @return {Boolean} This Bounds contains the given Point.
427
+ */
428
+ GBounds.prototype.containsPoint = function (point) {
429
+ var outer = this;
430
+ return (outer.minX <= point.x &&
431
+ outer.maxX >= point.x &&
432
+ outer.minY <= point.y &&
433
+ outer.maxY >= point.y);
434
+ };
435
+
436
+ /**
437
+ * Get a cell in the grid, creating it first if necessary.
438
+ *
439
+ * Optimization candidate
440
+ *
441
+ * @param {Number} x The x coordinate of the cell.
442
+ * @param {Number} y The y coordinate of the cell.
443
+ * @param {Number} z The z coordinate of the cell.
444
+ * @return {Array} The cell in the array.
445
+ */
446
+ MarkerManager.prototype.getGridCellCreate_ = function (x, y, z) {
447
+ var grid = this.grid_[z];
448
+ if (x < 0) {
449
+ x += this.gridWidth_[z];
450
+ }
451
+ var gridCol = grid[x];
452
+ if (!gridCol) {
453
+ gridCol = grid[x] = [];
454
+ return (gridCol[y] = []);
455
+ }
456
+ var gridCell = gridCol[y];
457
+ if (!gridCell) {
458
+ return (gridCol[y] = []);
459
+ }
460
+ return gridCell;
461
+ };
462
+
463
+
464
+ /**
465
+ * Get a cell in the grid, returning undefined if it does not exist.
466
+ *
467
+ * NOTE: Optimized for speed -- otherwise could combine with getGridCellCreate_.
468
+ *
469
+ * @param {Number} x The x coordinate of the cell.
470
+ * @param {Number} y The y coordinate of the cell.
471
+ * @param {Number} z The z coordinate of the cell.
472
+ * @return {Array} The cell in the array.
473
+ */
474
+ MarkerManager.prototype.getGridCellNoCreate_ = function (x, y, z) {
475
+ var grid = this.grid_[z];
476
+ if (x < 0) {
477
+ x += this.gridWidth_[z];
478
+ }
479
+ var gridCol = grid[x];
480
+ return gridCol ? gridCol[y] : undefined;
481
+ };
482
+
483
+
484
+ /**
485
+ * Turns at geographical bounds into a grid-space bounds.
486
+ *
487
+ * @param {LatLngBounds} bounds The geographical bounds.
488
+ * @param {Number} zoom The zoom level of the bounds.
489
+ * @param {GSize} swPadding The padding in pixels to extend beyond the
490
+ * given bounds.
491
+ * @param {GSize} nePadding The padding in pixels to extend beyond the
492
+ * given bounds.
493
+ * @return {GBounds} The bounds in grid space.
494
+ */
495
+ MarkerManager.prototype.getGridBounds_ = function (bounds, zoom, swPadding, nePadding) {
496
+ zoom = Math.min(zoom, this.maxZoom_);
497
+
498
+ var bl = bounds.getSouthWest();
499
+ var tr = bounds.getNorthEast();
500
+ var sw = this.getTilePoint_(bl, zoom, swPadding);
501
+ var ne = this.getTilePoint_(tr, zoom, nePadding);
502
+ var gw = this.gridWidth_[zoom];
503
+
504
+ // Crossing the prime meridian requires correction of bounds.
505
+ if (tr.lng() < bl.lng() || ne.x < sw.x) {
506
+ sw.x -= gw;
507
+ }
508
+ if (ne.x - sw.x + 1 >= gw) {
509
+ // Computed grid bounds are larger than the world; truncate.
510
+ sw.x = 0;
511
+ ne.x = gw - 1;
512
+ }
513
+ var gridBounds = new GBounds([sw, ne]);
514
+ gridBounds.z = zoom;
515
+ return gridBounds;
516
+ };
517
+
518
+
519
+ /**
520
+ * Gets the grid-space bounds for the current map viewport.
521
+ *
522
+ * @return {Bounds} The bounds in grid space.
523
+ */
524
+ MarkerManager.prototype.getMapGridBounds_ = function () {
525
+ var me = this;
526
+ return me.getGridBounds_(me.map_.getBounds(), me.mapZoom_, me.swPadding_, me.nePadding_);
527
+ };
528
+
529
+
530
+ /**
531
+ * Event listener for map:movend.
532
+ * NOTE: Use a timeout so that the user is not blocked
533
+ * from moving the map.
534
+ *
535
+ */
536
+ MarkerManager.prototype.onMapMoveEnd_ = function () {
537
+ var me = this;
538
+ me.objectSetTimeout_(this, this.updateMarkers_, 0);
539
+ };
540
+
541
+
542
+ /**
543
+ * Call a function or evaluate an expression after a specified number of
544
+ * milliseconds.
545
+ *
546
+ * Equivalent to the standard window.setTimeout function, but the given
547
+ * function executes as a method of this instance. So the function passed to
548
+ * objectSetTimeout can contain references to this.
549
+ * objectSetTimeout(this, function () { alert(this.x) }, 1000);
550
+ *
551
+ * @param {Object} object The target object.
552
+ * @param {Function} command The command to run.
553
+ * @param {Number} milliseconds The delay.
554
+ * @return {Boolean} Success.
555
+ */
556
+ MarkerManager.prototype.objectSetTimeout_ = function (object, command, milliseconds) {
557
+ return window.setTimeout(function () {
558
+ command.call(object);
559
+ }, milliseconds);
560
+ };
561
+
562
+
563
+ /**
564
+ * Is this layer visible?
565
+ *
566
+ * Returns visibility setting
567
+ *
568
+ * @return {Boolean} Visible
569
+ */
570
+ MarkerManager.prototype.visible = function () {
571
+ return this.show_ ? true : false;
572
+ };
573
+
574
+
575
+ /**
576
+ * Returns true if the manager is hidden.
577
+ * Otherwise returns false.
578
+ * @return {Boolean} Hidden
579
+ */
580
+ MarkerManager.prototype.isHidden = function () {
581
+ return !this.show_;
582
+ };
583
+
584
+
585
+ /**
586
+ * Shows the manager if it's currently hidden.
587
+ */
588
+ MarkerManager.prototype.show = function () {
589
+ this.show_ = true;
590
+ this.refresh();
591
+ };
592
+
593
+
594
+ /**
595
+ * Hides the manager if it's currently visible
596
+ */
597
+ MarkerManager.prototype.hide = function () {
598
+ this.show_ = false;
599
+ this.refresh();
600
+ };
601
+
602
+
603
+ /**
604
+ * Toggles the visibility of the manager.
605
+ */
606
+ MarkerManager.prototype.toggle = function () {
607
+ this.show_ = !this.show_;
608
+ this.refresh();
609
+ };
610
+
611
+
612
+ /**
613
+ * Refresh forces the marker-manager into a good state.
614
+ * <ol>
615
+ * <li>If never before initialized, shows all the markers.</li>
616
+ * <li>If previously initialized, removes and re-adds all markers.</li>
617
+ * </ol>
618
+ */
619
+ MarkerManager.prototype.refresh = function () {
620
+ var me = this;
621
+ if (me.shownMarkers_ > 0) {
622
+ me.processAll_(me.shownBounds_, me.removeOverlay_);
623
+ }
624
+ // An extra check on me.show_ to increase performance (no need to processAll_)
625
+ if (me.show_) {
626
+ me.processAll_(me.shownBounds_, me.addOverlay_);
627
+ }
628
+ me.notifyListeners_();
629
+ };
630
+
631
+
632
+ /**
633
+ * After the viewport may have changed, add or remove markers as needed.
634
+ */
635
+ MarkerManager.prototype.updateMarkers_ = function () {
636
+ var me = this;
637
+ me.mapZoom_ = this.map_.getZoom();
638
+ var newBounds = me.getMapGridBounds_();
639
+
640
+ // If the move does not include new grid sections,
641
+ // we have no work to do:
642
+ if (newBounds.equals(me.shownBounds_) && newBounds.z === me.shownBounds_.z) {
643
+ return;
644
+ }
645
+
646
+ if (newBounds.z !== me.shownBounds_.z) {
647
+ me.processAll_(me.shownBounds_, me.removeOverlay_);
648
+ if (me.show_) { // performance
649
+ me.processAll_(newBounds, me.addOverlay_);
650
+ }
651
+ } else {
652
+ // Remove markers:
653
+ me.rectangleDiff_(me.shownBounds_, newBounds, me.removeCellMarkers_);
654
+
655
+ // Add markers:
656
+ if (me.show_) { // performance
657
+ me.rectangleDiff_(newBounds, me.shownBounds_, me.addCellMarkers_);
658
+ }
659
+ }
660
+ me.shownBounds_ = newBounds;
661
+
662
+ me.notifyListeners_();
663
+ };
664
+
665
+
666
+ /**
667
+ * Notify listeners when the state of what is displayed changes.
668
+ */
669
+ MarkerManager.prototype.notifyListeners_ = function () {
670
+ GEvent.trigger(this, "changed", this.shownBounds_, this.shownMarkers_);
671
+ };
672
+
673
+
674
+ /**
675
+ * Process all markers in the bounds provided, using a callback.
676
+ *
677
+ * @param {Bounds} bounds The bounds in grid space.
678
+ * @param {Function} callback The function to call for each marker.
679
+ */
680
+ MarkerManager.prototype.processAll_ = function (bounds, callback) {
681
+ for (var x = bounds.minX; x <= bounds.maxX; x++) {
682
+ for (var y = bounds.minY; y <= bounds.maxY; y++) {
683
+ this.processCellMarkers_(x, y, bounds.z, callback);
684
+ }
685
+ }
686
+ };
687
+
688
+
689
+ /**
690
+ * Process all markers in the grid cell, using a callback.
691
+ *
692
+ * @param {Number} x The x coordinate of the cell.
693
+ * @param {Number} y The y coordinate of the cell.
694
+ * @param {Number} z The z coordinate of the cell.
695
+ * @param {Function} callback The function to call for each marker.
696
+ */
697
+ MarkerManager.prototype.processCellMarkers_ = function (x, y, z, callback) {
698
+ var cell = this.getGridCellNoCreate_(x, y, z);
699
+ if (cell) {
700
+ for (var i = cell.length - 1; i >= 0; i--) {
701
+ callback(cell[i]);
702
+ }
703
+ }
704
+ };
705
+
706
+
707
+ /**
708
+ * Remove all markers in a grid cell.
709
+ *
710
+ * @param {Number} x The x coordinate of the cell.
711
+ * @param {Number} y The y coordinate of the cell.
712
+ * @param {Number} z The z coordinate of the cell.
713
+ */
714
+ MarkerManager.prototype.removeCellMarkers_ = function (x, y, z) {
715
+ this.processCellMarkers_(x, y, z, this.removeOverlay_);
716
+ };
717
+
718
+
719
+ /**
720
+ * Add all markers in a grid cell.
721
+ *
722
+ * @param {Number} x The x coordinate of the cell.
723
+ * @param {Number} y The y coordinate of the cell.
724
+ * @param {Number} z The z coordinate of the cell.
725
+ */
726
+ MarkerManager.prototype.addCellMarkers_ = function (x, y, z) {
727
+ this.processCellMarkers_(x, y, z, this.addOverlay_);
728
+ };
729
+
730
+
731
+ /**
732
+ * Use the rectangleDiffCoords_ function to process all grid cells
733
+ * that are in bounds1 but not bounds2, using a callback, and using
734
+ * the current MarkerManager object as the instance.
735
+ *
736
+ * Pass the z parameter to the callback in addition to x and y.
737
+ *
738
+ * @param {Bounds} bounds1 The bounds of all points we may process.
739
+ * @param {Bounds} bounds2 The bounds of points to exclude.
740
+ * @param {Function} callback The callback function to call
741
+ * for each grid coordinate (x, y, z).
742
+ */
743
+ MarkerManager.prototype.rectangleDiff_ = function (bounds1, bounds2, callback) {
744
+ var me = this;
745
+ me.rectangleDiffCoords_(bounds1, bounds2, function (x, y) {
746
+ callback.apply(me, [x, y, bounds1.z]);
747
+ });
748
+ };
749
+
750
+
751
+ /**
752
+ * Calls the function for all points in bounds1, not in bounds2
753
+ *
754
+ * @param {Bounds} bounds1 The bounds of all points we may process.
755
+ * @param {Bounds} bounds2 The bounds of points to exclude.
756
+ * @param {Function} callback The callback function to call
757
+ * for each grid coordinate.
758
+ */
759
+ MarkerManager.prototype.rectangleDiffCoords_ = function (bounds1, bounds2, callback) {
760
+ var minX1 = bounds1.minX;
761
+ var minY1 = bounds1.minY;
762
+ var maxX1 = bounds1.maxX;
763
+ var maxY1 = bounds1.maxY;
764
+ var minX2 = bounds2.minX;
765
+ var minY2 = bounds2.minY;
766
+ var maxX2 = bounds2.maxX;
767
+ var maxY2 = bounds2.maxY;
768
+
769
+ var x, y;
770
+ for (x = minX1; x <= maxX1; x++) { // All x in R1
771
+ // All above:
772
+ for (y = minY1; y <= maxY1 && y < minY2; y++) { // y in R1 above R2
773
+ callback(x, y);
774
+ }
775
+ // All below:
776
+ for (y = Math.max(maxY2 + 1, minY1); // y in R1 below R2
777
+ y <= maxY1; y++) {
778
+ callback(x, y);
779
+ }
780
+ }
781
+
782
+ for (y = Math.max(minY1, minY2);
783
+ y <= Math.min(maxY1, maxY2); y++) { // All y in R2 and in R1
784
+ // Strictly left:
785
+ for (x = Math.min(maxX1 + 1, minX2) - 1;
786
+ x >= minX1; x--) { // x in R1 left of R2
787
+ callback(x, y);
788
+ }
789
+ // Strictly right:
790
+ for (x = Math.max(minX1, maxX2 + 1); // x in R1 right of R2
791
+ x <= maxX1; x++) {
792
+ callback(x, y);
793
+ }
794
+ }
795
+ };
796
+
797
+
798
+ /**
799
+ * Removes value from array. O(N).
800
+ *
801
+ * @param {Array} array The array to modify.
802
+ * @param {any} value The value to remove.
803
+ * @param {Boolean} opt_notype Flag to disable type checking in equality.
804
+ * @return {Number} The number of instances of value that were removed.
805
+ */
806
+ MarkerManager.prototype.removeFromArray_ = function (array, value, opt_notype) {
807
+ var shift = 0;
808
+ for (var i = 0; i < array.length; ++i) {
809
+ if (array[i] === value || (opt_notype && array[i] === value)) {
810
+ array.splice(i--, 1);
811
+ shift++;
812
+ }
813
+ }
814
+ return shift;
815
+ };
data/lib/map.rb CHANGED
@@ -2,7 +2,7 @@ module Refinery
2
2
  module Map
3
3
  class << self
4
4
  def version
5
- %q{0.0.1}
5
+ %q{0.0.2}
6
6
  end
7
7
  end
8
8
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: refinerycms-map
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
8
  - 0
9
- - 1
10
- version: 0.0.1
9
+ - 2
10
+ version: 0.0.2
11
11
  platform: ruby
12
12
  authors:
13
13
  - Envy Labs
@@ -30,12 +30,16 @@ extra_rdoc_files: []
30
30
  files:
31
31
  - app/controllers/admin/map_locations_controller.rb
32
32
  - app/controllers/map_locations_controller.rb
33
+ - app/models/geocode.rb
33
34
  - app/models/map_location.rb
34
35
  - app/views/admin/map_locations/_form.html.erb
35
36
  - app/views/admin/map_locations/_map_location.html.erb
36
37
  - app/views/admin/map_locations/edit.html.erb
37
38
  - app/views/admin/map_locations/index.html.erb
38
39
  - app/views/admin/map_locations/new.html.erb
40
+ - app/views/map_locations/_locations.html.erb
41
+ - app/views/map_locations/_map.html.erb
42
+ - app/views/map_locations/_search.html.erb
39
43
  - app/views/map_locations/index.html.erb
40
44
  - app/views/map_locations/show.html.erb
41
45
  - config/locales/en.yml
@@ -44,6 +48,7 @@ files:
44
48
  - generators/map/templates/jquery.jmapping.min.js
45
49
  - generators/map/templates/jquery.metadata.js
46
50
  - generators/map/templates/mapiconmaker.js
51
+ - generators/map/templates/markermanager.js
47
52
  - generators/map/templates/migration.rb
48
53
  - lib/gemspec.rb
49
54
  - lib/map.rb