/** * MIT License * Copyright(c) 2013 essoduke.org * * Permission is hereby granted, free of charge, to any person obtaining a copy of this software * and associated documentation files (the "Software"), to deal in the Software without restriction, * including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, * and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED 『AS IS』, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * * jQuery tinyMap 輕鬆建立 Google Maps 的 jQuery 擴充套件 * 短小精幹!拯救你免於 Google Maps API 的摧殘,輕鬆建立 Google Maps 的 jQuery 擴充套件。 * http://app.essoduke.org/tinyMap/ * * @author: Essoduke Chang * @version: 2.3.1 * * [Changelog] * 修正 marker 使用地址字串會造成無法顯示的錯誤。 * * Last Modify: Thu, 11 July 2013 07:40:58 GMT */ ;(function ($) { 'use strict'; // Default settings var defaults = { 'center': {x: '24', y: '121'}, 'control': true, 'draggable': true, 'keyboardShortcuts': true, 'mapTypeControl': true, 'mapTypeControlOptions': { 'position': 'TOP_RIGHT', 'style': 'DEFAULT' }, 'mapTypeId': 'ROADMAP', 'marker': [], 'markerFitBounds': false, 'polyline': [], 'navigationControl': true, 'navigationControlOptions': { 'position': 'TOP_LEFT', 'style': 'DEFAULT' }, 'scaleControl': true, 'scaleControlOptions': { 'position': 'BOTTOM_LEFT', 'style': 'DEFAULT' }, 'scrollwheel': true, 'zoom': 4, 'notfound': '找不到查詢的地點', 'loading': '讀取中…', 'kml': { 'url': '', 'viewport': true, 'infowindow': false } }; // Loop counter for geocoder var loop = 0; // Label in Maps function Label (options) { var css = (options.css || ''); this.setValues(options); this.span = $('').css({'position': 'relative', 'left': '-50%', 'top': '0', 'white-space': 'nowrap'}).addClass(css); this.div = $('
').css({'position': 'absolute', 'display': 'none'}); this.span.appendTo(this.div); } Label.prototype = new google.maps.OverlayView(); Label.prototype.onAdd = function () { var pane = this.getPanes().overlayLayer, me = this; this.div.appendTo($(pane)); this.listeners = [ google.maps.event.addListener(this, 'position_changed', function () { me.draw(); }), google.maps.event.addListener(this, 'text_changed', function() { me.draw(); }) ]; }; Label.prototype.draw = function () { var projection = this.getProjection(), position = projection.fromLatLngToDivPixel(this.get('position')); this.div.css({'left': position.x + 'px', 'top': position.y + 'px', 'display': 'block'}); if (this.text) { this.span.html(this.text.toString()); } }; /** * tinyMap Constructor * @param container {object} HTML element * @param options {object|string| User settings * @constructor */ function tinyMap (container, options) { // Make sure the API has loaded. if (!window.hasOwnProperty('google')) { return; } // Map instance this.map = null; this.container = container; this.options = $.extend({}, defaults, options); // Google map options this.GoogleMapOptions = { 'center': new google.maps.LatLng(this.options.center.x, this.options.center.y), 'control': this.options.control, 'draggable': this.options.draggable, 'keyboardShortcuts': this.options.keyboardShortcuts, 'mapTypeId': google.maps.MapTypeId[this.options.mapTypeId.toUpperCase()], 'mapTypeControl': this.options.mapTypeControl, 'mapTypeControlOptions': { 'position': google.maps.ControlPosition[this.options.mapTypeControlOptions.position], 'style': google.maps.MapTypeControlStyle[this.options.mapTypeControlOptions.style.toUpperCase()] }, 'navigationControl': this.options.navigationControl, 'navigationControlOptions': { 'position': google.maps.ControlPosition[this.options.navigationControlOptions.position], 'style': google.maps.NavigationControlStyle[this.options.navigationControlOptions.style.toUpperCase()] }, 'scaleControl': this.options.scaleControl, 'scaleControlOptions': { 'position': google.maps.ControlPosition[this.options.scaleControlOptions.position], 'style': google.maps.ScaleControlStyle[this.options.scaleControlOptions.style.toUpperCase()] }, 'scrollwheel': this.options.scrollwheel, 'zoom': this.options.zoom }; this.init(); } /** * Initialize * @this (tinyMap) */ tinyMap.prototype.init = function () { var bounds, self = this; loop += 1; if ('string' === typeof this.options.center) { window.setTimeout(function () { var geocoder = new google.maps.Geocoder(), error = $(self.container), msg = ''; geocoder.geocode({'address': self.options.center}, function (results, status) { try { if (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) { self.init(); } else if (status === google.maps.GeocoderStatus.OK && 0 !== results.length) { self.GoogleMapOptions.center = (status === google.maps.GeocoderStatus.OK && 0 !== results.length) ? results[0].geometry.location : ''; self.map = new google.maps.Map(self.container, self.GoogleMapOptions); bounds = self.overlay(); if (self.options.marker.length && true === self.options.markerFitBounds) { self.map.fitBounds(bounds); } } else { msg = self.options.notfound.text || status; error.html(msg.replace(//g, '>')); } } catch (err) { error.html((undefined !== err.message ? err.message : err.description).toString()); } }); }, 200 * loop); } else { this.map = new google.maps.Map(this.container, this.GoogleMapOptions); bounds = this.overlay(); if (this.options.marker.length && true === this.options.markerFitBounds) { this.map.fitBounds(bounds); } } }; /** * Overlay * @this (tinyMap) * @return {object} bounds object */ tinyMap.prototype.overlay = function () { var bounds = new google.maps.LatLngBounds(), opt = this.options, coords = [], kml, kml_url = '', kml_opt = {}, polygon, polyline, circle, circles, c, p, d, m; // KML if (undefined !== opt.kml) { kml_opt = { preserveViewport: true, suppressInfoWindows: false }; kml_url = ('string' === typeof opt.kml && 0 !== opt.kml.length) ? opt.kml : (undefined !== opt.kml.url ? opt.kml.url : ''); kml = new google.maps.KmlLayer(kml_url, $.extend(kml_opt, opt.kml)); kml.setMap(this.map); } // direction if (undefined !== opt.direction) { if (0 < opt.direction.length) { for (d in opt.direction) { if (opt.direction.hasOwnProperty(d)) { if (undefined !== opt.direction[d]) { this.direction(opt.direction[d]); } } } } } // Markers if (undefined !== opt.marker) { if (0 < opt.marker.length) { for (m in opt.marker) { if (opt.marker.hasOwnProperty(m)) { if ((opt.marker[m].hasOwnProperty('addr'))) { if ('object' === typeof opt.marker[m].addr) { if (2 === opt.marker[m].addr.length) { this.markerDirect(opt.marker[m], bounds); } } else { this.markerByGeocoder(opt.marker[m], bounds); } } } } } } // polyline if (undefined !== opt.polyline) { if (undefined !== opt.polyline.coords) { for (p in opt.polyline.coords) { if (opt.polyline.coords.hasOwnProperty(p)) { c = opt.polyline.coords; if (undefined !== c[p]) { coords.push(new google.maps.LatLng(c[p][0], c[p][1])); } } } polyline = new google.maps.Polyline({ 'path': coords, 'strokeColor': opt.polyline.color || '#FF0000', 'strokeOpacity': 1.0, 'strokeWeight': opt.polyline.width || 2 }); polyline.setMap(this.map); } } // polygon if (undefined !== opt.polygon) { if (undefined !== opt.polygon.coords) { for (p in opt.polygon.coords) { if (opt.polygon.coords.hasOwnProperty(p)) { c = opt.polygon.coords; if (undefined !== c[p]) { coords.push(new google.maps.LatLng(c[p][0], c[p][1])); } } } polygon = new google.maps.Polygon({ 'path': coords, 'strokeColor': opt.polyline.color || '#FF0000', 'strokeOpacity': 1.0, 'strokeWeight': opt.polygon.width || 2, 'fillColor': opt.polygon.fillcolor || '#CC0000', 'fillOpacity': 0.35 }); polygon.setMap(this.map); if ($.isFunction(opt.polygon.click)) { google.maps.event.addListener(polygon, 'click', opt.polygon.click); } } } // circle if (undefined !== opt.circle && 0 < opt.circle.length) { for (c = opt.circle.length - 1; c >= 0; c -= 1) { circle = opt.circle[c]; if (undefined !== circle.center.x && undefined !== circle.center.y) { circles = new google.maps.Circle({ 'strokeColor': circle.color || '#FF0000', 'strokeOpacity': circle.opacity || 0.8, 'strokeWeight': circle.width || 2, 'fillColor': circle.fillcolor || '#FF0000', 'fillOpacity': circle.fillopacity || 0.35, 'map': this.map, 'center': new google.maps.LatLng(circle.center.x, circle.center.y), 'radius': circle.radius || 10 }); if ($.isFunction(opt.circle[c].click)) { google.maps.event.addListener(circles, 'click', opt.circle[c].click); } } } } return bounds; }; /** * Set a marker directly by latitude and longitude * @param opt {object} options * @param bounds {object} bounds object * @this (tinyMap) */ tinyMap.prototype.markerDirect = function (opt, bounds) { var marker, label_opt, label, infoWindow = new google.maps.InfoWindow({content: opt.text}), markerOptions = { 'map': this.map, 'position': new google.maps.LatLng(opt.addr[0], opt.addr[1]), 'title': opt.text.replace(/<([^>]+)>/g, '') }; if ('string' === typeof opt.icon) { markerOptions.icon = opt.icon; } marker = new google.maps.Marker(markerOptions); // autozoom if (bounds) { if (marker.hasOwnProperty('position')) { if (marker.position.hasOwnProperty('jb') && marker.position.hasOwnProperty) { bounds.extend(markerOptions.position); } } } label_opt = { map: this.map, css: opt.css || '' }; if ('string' === typeof opt.label && 0 !== opt.label.length) { label_opt.text = opt.label; } label = new Label(label_opt); label.bindTo('position', marker, 'position'); label.bindTo('text', marker, 'position'); google.maps.event.addListener(marker, 'click', function () { infoWindow.open(this.map, marker); }); return bounds; }; /** * Set a marker by Geocoder service * @param opt {object} options * @param bounds {object} bounds object * @this (tinyMap) */ tinyMap.prototype.markerByGeocoder = function (opt, bounds) { var geocoder = new google.maps.Geocoder(), self = this; geocoder.geocode({'address': opt.addr}, function (results, status) { // if exceeded limit, then call again; if (status === google.maps.GeocoderStatus.OVER_QUERY_LIMIT) { window.setTimeout(function () { this.geocoder(self.map, opt, bounds, false); }, 200); } else if (status === google.maps.GeocoderStatus.OK) { var marker, label_opt, label, infoWindow = new google.maps.InfoWindow({content: opt.text}), markerOptions = { 'map': self.map, 'position': results[0].geometry.location, 'title': opt.text.replace(/<([^>]+)>/g, '') }; if ('string' === typeof opt.icon) { markerOptions.icon = opt.icon; } marker = new google.maps.Marker(markerOptions); // autozoom if (bounds) { if (marker.hasOwnProperty('position')) { if (marker.position.hasOwnProperty('jb') && marker.position.hasOwnProperty('kb')) { bounds.extend(markerOptions.position); } } } label_opt = { map: self.map, css: opt.css || '' }; if ('string' === typeof opt.label && 0 !== opt.label.length) { label_opt.text = opt.label; } label = new Label(label_opt); label.bindTo('position', marker, 'position'); label.bindTo('text', marker, 'position'); google.maps.event.addListener(marker, 'click', function () { infoWindow.open(self.map, marker); }); } }); }; /** * Direction service * @param opt {object} options * @this (tinyMap) */ tinyMap.prototype.direction = function (opt) { var waypoints = [], directionsService = new google.maps.DirectionsService(), directionsDisplay = new google.maps.DirectionsRenderer(), request = { 'travelMode': google.maps.DirectionsTravelMode.DRIVING, 'optimizeWaypoints': opt.optimize || false }, i = 0, c = 0; if ('string' === typeof opt.from) { request.origin = opt.from; } if ('string' === typeof opt.to) { request.destination = opt.to; } if ('string' === typeof opt.travel) { if (0 < opt.travel.length) { request.travelMode = google.maps.DirectionsTravelMode[opt.travel.toUpperCase()]; } } if (undefined !== opt.waypoint && 0 !== opt.waypoint) { for (i = 0, c = opt.waypoint.length; i < c; i += 1) { waypoints.push({ 'location': opt.waypoint[i].toString(), 'stopover': true }); } request.waypoints = waypoints; } if (undefined !== request.origin && undefined !== request.destination) { directionsService.route(request, function (response, status) { if (status === google.maps.DirectionsStatus.OK) { directionsDisplay.setDirections(response); } }); directionsDisplay.setMap(this.map); } }; /** * jQuery tinyMap instance * @param options {object} Settings * @public */ $.fn.tinyMap = function (options) { return this.each(function () { if (!$.data(this, 'tinyMap')) { $.data(this, 'tinyMap', new tinyMap(this, options)); } }); }; /** * Google Maps PanTo method * @param addr {string} Text address or "latitude, longitude" format * @public */ $.fn.tinyMapPanTo = function (addr) { return this.each(function () { var obj = $(this), data = obj.data('tinyMap'), map = data.hasOwnProperty('map') ? data.map : null, geocoder = new google.maps.Geocoder(); if (map) { if (-1 !== addr.indexOf(',')) { addr = 'loc: ' + addr; } geocoder.geocode({'address': addr}, function (results, status) { if (status === google.maps.GeocoderStatus.OK) { if ($.isFunction(map.panTo)) { map.panTo(results[0].geometry.location); } } }); } }); }; })(jQuery);