refinerycms-map 0.0.1 → 0.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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