Skip to content

Instantly share code, notes, and snippets.

@dieface
Last active January 1, 2016 08:39
Show Gist options
  • Save dieface/8120113 to your computer and use it in GitHub Desktop.
Save dieface/8120113 to your computer and use it in GitHub Desktop.
/**
* 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.5.2
*
* [Changelog]
* 修正 modify 方法無法改變 zoom 縮放等級的問題。
* 修正 clear 方法未清除 Label 的錯誤。
*
* Last Modify: Wed, 4 September 2013 06:40:45 GMT
*/
;(function ($, window, document, undefined) {
//'use strict';
// Loop counter for geocoder
var pluginName = 'tinyMap',
loop = 0,
// Plugin default settings
defaults = {
'center': {x: '24', y: '121'},
'control': true,
'disableDefaultUI': false, //2.5.1
'draggable': true,
'keyboardShortcuts': true,
'mapTypeControl': true,
'mapTypeControlOptions': {
'position': 'TOP_RIGHT',
'style': 'DEFAULT'
},
'mapTypeId': 'ROADMAP',
'marker': [],
'markerFitBounds': false,
'maxZoom': null, //2.5.1
'minZoom': null, //2.5.1
'panControl': true, //2.5.1
'panControlOptions': {
'position': 'LEFT_TOP'
},
'polyline': [],
'navigationControl': true,
'navigationControlOptions': {
'position': 'TOP_LEFT',
'style': 'DEFAULT'
},
'scaleControl': true,
'scaleControlOptions': {
'position': 'BOTTOM_LEFT',
'style': 'DEFAULT'
},
'scrollwheel': true,
'streetViewControl': true, //2.5.1
'streetViewControlOptions': {
'position': 'LEFT_TOP'
},
'zoom': 4,
'zoomControl': true,
'zoomControlOptions': {
'style': 'LARGE',
'position': 'LEFT_TOP'
},
'notfound': '找不到查詢的地點',
'loading': '讀取中…',
'kml': {
'url': '',
'viewport': true,
'infowindow': false
},
'interval': 200 //2.5.0
};
/**
* _hasOwnProperty for compatibility IE
* @param {Object} obj Object
* @param {string} property Property name
* @return {boolean}
* @version 2.4.3
*/
function _hasOwnProperty(obj, property) {
try {
return (!window.hasOwnProperty) ? Object.prototype.hasOwnProperty.call(obj, property.toString()) : obj.hasOwnProperty(property.toString());
} catch (ignore) {
}
}
//#!#CLASS_LABEL
/**
* Label in Maps
* @param {Object} options Label options
* @constructor
*/
function Label (options) {
var css = (options.css || '');
this.setValues(options);
this.span = $('<span/>').css({'position': 'relative', 'left': '-50%', 'top': '0', 'white-space': 'nowrap'}).addClass(css);
this.div = $('<div/>').css({'position': 'absolute', 'display': 'none'});
this.span.appendTo(this.div);
}
/**
* Inherit from Google Maps OverlayView
* @this {Label}
*/
Label.prototype = new google.maps.OverlayView();
/**
* binding the customize events to map
* @this {Label}
*/
Label.prototype.onAdd = function () {
var pane = this.getPanes().overlayLayer, me = this;
this.div.appendTo($(pane));
this.listeners = [
google.maps.event.addListener(this, 'visible_changed', me.onRemove)
];
};
/**
* Label draw to map
* @this {Label}
*/
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());
}
};
/**
* Label remove from the map
* @this {Label}
*/
Label.prototype.onRemove = function () {
$(this.div).remove();
};
//#!#END
/**
* tinyMap Constructor
* @param {Object} container HTML element
* @param {(Object|string)} options User settings
* @constructor
*/
function tinyMap (container, options) {
//function tinyMap (container, options) {
// Make sure the API has loaded.
if (!_hasOwnProperty(window, 'google')) {
return;
}
/**
* Map instance
* @type {Object}
*/
this.map = null;
/**
* DOM of selector
* @type {Object}
*/
this.container = container;
/**
* Merge the options
* @type {Object}
*/
this.options = $.extend({}, defaults, options);
/**
* Interval for geocoder's query interval
* @type {number}
*/
this.interval = parseInt(this.options.interval, 10);
this.interval = isNaN(this.interval) ? 200 : this.interval;
/**
* Google Maps options
* @type {Object}
*/
this.GoogleMapOptions = {
'center': new google.maps.LatLng(this.options.center.x, this.options.center.y),
'control': this.options.control,
'disableDefaultUI': this.options.disableDefaultUI,
'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()]
},
'maxZoom': this.options.maxZoom,
'minZoom': this.options.minZoom,
'navigationControl': this.options.navigationControl,
'navigationControlOptions': {
'position': google.maps.ControlPosition[this.options.navigationControlOptions.position],
'style': google.maps.NavigationControlStyle[this.options.navigationControlOptions.style.toUpperCase()]
},
'panControl': this.options.panControl,
'panControlOptions': {
'position': google.maps.ControlPosition[this.options.panControlOptions.position]
},
'rotateControl': this.options.rotateControl,
'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,
'streetViewControl': this.options.streetViewControl,
'streetViewControlOptions': {
'position': google.maps.ControlPosition[this.options.streetViewControlOptions.position]
},
'zoom': this.options.zoom,
'zoomControl': this.options.zoomControl,
'zoomControlOptions': {
'position': google.maps.ControlPosition[this.options.zoomControlOptions.position],
'style': google.maps.ZoomControlStyle[this.options.zoomControlOptions.style.toUpperCase()]
}
};
this.init();
}
/**
* tinyMap prototype
*/
tinyMap.prototype = {
VERSION: '2.5.2',
// Layers container
_markers: [],
_labels: [],
_polylines: [],
_polygons: [],
_circles: [],
_kmls: [],
_directions: [],
// Google Maps LatLngClass
bounds: new google.maps.LatLngBounds(),
/**
* Set zoom level of the map
* @param {Object} map Map instance
* @param {Object} opt tinyMap options
*/
setZoom: function (map, opt) {
if (_hasOwnProperty(opt, 'zoom') && map) {
map.setZoom(opt.zoom);
}
},
//#!#FUNC_KML
/**
* KML overlay
* @param {Object} map Map instance
* @param {Object} opt KML options
*/
kml: function (map, opt) {
var opt = (!opt ? this.options : opt), kml_opt, kml_url, 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));
this._kmls.push(kml);
kml.setMap(map);
}
},
//#!#END
//#!#FUNC_DIRECTION
/**
* Direction overlay
* @param {Object} map Map instance
* @param {Object} opt Direction options
*/
direction: function (map, opt) {
var d, opt = !opt ? this.options : opt;
if (undefined !== opt.direction && 0 < opt.direction.length) {
for (d in opt.direction) {
if (_hasOwnProperty(opt.direction, d)) {
if (undefined !== opt.direction[d]) {
this.DirectionService(opt.direction[d]);
}
}
}
}
},
//#!#END
//#!#FUNC_MARKERS
/**
* Markers overlay
* @param {Object} map Map instance
* @param {Object} opt Markers options
*/
markers: function (map, opt) {
var m, opt = !opt ? this.options : opt;
if (undefined !== opt.marker) {
if (0 < opt.marker.length) {
for (m in opt.marker) {
if (_hasOwnProperty(opt.marker, m)) {
if (_hasOwnProperty(opt.marker[m], 'addr')) {
if ('object' === typeof opt.marker[m].addr) {
if (2 === opt.marker[m].addr.length) {
this.MarkerDirect(opt.marker[m]);
}
} else {
this.MarkerByGeocoder(opt.marker[m]);
}
}
}
}
}
}
},
//#!#END
//#!#FUNC_POLYLINE
/**
* Polyline overlay
* @param {Object} map Map instance
* @param {Object} opt Polyline options
*/
DrawPolyline: function (map, opt) {
var polyline, p, c, coords = [], opt = !opt ? this.options : opt;
if (undefined !== opt.polyline) {
if (undefined !== opt.polyline.coords) {
for (p in opt.polyline.coords) {
if (_hasOwnProperty(opt.polyline.coords, 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
});
this._polylines.push(polyline);
polyline.setMap(map);
}
}
},
//#!#END
//#!#FUNC_POLYGON
/**
* Polygon overlay
* @param {Object} map Map instance
* @param {Object} opt Polygon options
*/
DrawPolygon: function (map, opt) {
var polygon, p, c, coords = [], opt = !opt ? this.options : opt;
if (undefined !== opt.polygon) {
if (undefined !== opt.polygon.coords) {
for (p in opt.polygon.coords) {
if (_hasOwnProperty(opt.polygon.coords, 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.polygon.color || '#FF0000',
'strokeOpacity': 1.0,
'strokeWeight': opt.polygon.width || 2,
'fillColor': opt.polygon.fillcolor || '#CC0000',
'fillOpacity': 0.35
});
this._polygons.push(polygon);
polygon.setMap(this.map);
if ($.isFunction(opt.polygon.click)) {
google.maps.event.addListener(polygon, 'click', opt.polygon.click);
}
}
}
},
//#!#END
//#!#FUNC_CIRCLE
/**
* Circle overlay
* @param {Object} map Map instance
* @param {Object} opt Circle options
*/
DrawCircle: function (map, opt) {
var c, circle, circles, opt = !opt ? this.options : opt;
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,
'zIndex': 100
});
this._circles.push(circles);
if ($.isFunction(opt.circle[c].click)) {
google.maps.event.addListener(circles, 'click', opt.circle[c].click);
}
}
}
}
},
//#!#END
/**
* Overlay process
* @this {tinyMap}
*/
overlay: function () {
//#!#CALL_KML
// kml overlay
this.kml(this.map);
//#!#END
//#!#CALL_DIRECTION
// direction overlay
this.direction(this.map);
//#!#END
//#!#CALL_MARKERS
// markers overlay
this.markers(this.map);
//#!#END
//#!#CALL_POLYLINE
// polyline overlay
this.DrawPolyline(this.map);
//#!#END
//#!#CALL_POLYGON
// polygon overlay
this.DrawPolygon(this.map);
//#!#END
//#!#CALL_CIRCLE
// circle overlay
this.DrawCircle(this.map);
//#!#END
},
//#!#PROTOTYPE_MARKERS
/**
* Set a marker directly by latitude and longitude
* @param {Object} opt Options
* @this {tinyMap}
*/
MarkerDirect: function (opt) {
var self = this,
marker, label_opt, label,
markerOptions = {
'map': this.map,
'position': new google.maps.LatLng(opt.addr[0], opt.addr[1]),
'title': opt.text.replace(/<([^>]+)>/g, ''),
'infoWindow': new google.maps.InfoWindow({content: opt.text})
};
if ('string' === typeof opt.icon) {
markerOptions.icon = opt.icon;
}
marker = new google.maps.Marker(markerOptions);
this._markers.push(marker);
// autozoom
if (_hasOwnProperty(marker, 'position')) {
if (marker.getPosition().lat() && marker.getPosition().lng()) {
self.bounds.extend(markerOptions.position);
}
}
label_opt = {
map: this.map,
css: undefined !== opt.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');
label.bindTo('visible', marker);
google.maps.event.addListener(marker, 'click', function () {
marker.infoWindow.open(self.map, marker);
});
},
/**
* Set a marker by Geocoder service
* @param {Object} opt Options
* @this {tinyMap}
*/
MarkerByGeocoder: function (opt) {
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 () {
self.MarkerByGeocoder(opt);
}, this.interval);
} else if (status === google.maps.GeocoderStatus.OK) {
var marker, label_opt, label,
markerOptions = {
'map': self.map,
'position': results[0].geometry.location,
'title': opt.text.replace(/<([^>]+)>/g, ''),
'infoWindow': new google.maps.InfoWindow({content: opt.text})
};
if ('string' === typeof opt.icon) {
markerOptions.icon = opt.icon;
}
marker = new google.maps.Marker(markerOptions);
self._markers.push(marker);
// autozoom
if (_hasOwnProperty(marker, 'position')) {
if (marker.getPosition().lat() && marker.getPosition().lng()) {
self.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');
label.bindTo('visible', marker);
google.maps.event.addListener(marker, 'click', function () {
marker.infoWindow.open(self.map, marker);
});
}
});
},
//#!#END
//#!#PROTOTYPE_DIRECTION
/**
* Direction service
* @param {Object} opt Options
* @this {tinyMap}
*/
DirectionService: 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);
this._directions.push(directionsDisplay);
}
},
//#!#END
/**
* tinyMap Initialize
* @this {tinyMap}
*/
init: function () {
var self = this, geocoder;
loop += 1;
if ('string' === typeof this.options.center) {
window.setTimeout(function () {
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);
console.log(00);
var pinkParksStyles = [
{
stylers: [
{ saturation: -99 },
{ lightness: -10 },
]
}
];
// Create a new StyledMapType object, passing it the array of styles,
// as well as the name to be displayed on the map type control.
var pinkMapType = new google.maps.StyledMapType(pinkParksStyles,
{name: ""});
//Associate the styled map with the MapTypeId and set it to display.
self.map.mapTypes.set('pink_parks', pinkMapType);
console.log(21);
//self.map.setMapTypeId('pink_parks');
//console.log(12);
self.overlay();
if (self.options.marker.length && true === self.options.markerFitBounds) {
setTimeout(function () {
self.map.fitBounds(self.bounds);
}, this.interval);
}
} else {
msg = self.options.notfound.text || status;
error.html(msg.replace(/</g, '&lt;').replace(/>/g, '&gt;'));
}
} catch (ignore) {
error.html((undefined !== ignore.message ? ignore.message : ignore.description).toString());
}
});
}, (this.interval * loop));
} else {
self.map = new google.maps.Map(self.container, self.GoogleMapOptions);
console.log(00);
var pinkParksStyles = [
{
stylers: [
{ saturation: -99 },
{ lightness: -10 },
]
}
];
// Create a new StyledMapType object, passing it the array of styles,
// as well as the name to be displayed on the map type control.
var pinkMapType = new google.maps.StyledMapType(pinkParksStyles,
{name: ""});
//Associate the styled map with the MapTypeId and set it to display.
self.map.mapTypes.set('pink_parks', pinkMapType);
console.log(21);
//self.map.setMapTypeId('pink_parks');
//console.log(12);
self.overlay();
if (self.options.marker.length && true === self.options.markerFitBounds) {
self.map.fitBounds(self.bounds);
}
}
},
/**
* Method: Google Maps PanTo
* @param {string} addr Text address or "latitude, longitude" format
* @public
*/
panto: function (addr) {
var self = this, latlng, geocoder;
if (_hasOwnProperty(self, 'map')) {
if (null !== self.map && undefined !== self.map) {
if ('string' === typeof addr) {
if (-1 !== addr.indexOf(',')) {
latlng = 'loc: ' + addr;
}
geocoder = new google.maps.Geocoder();
geocoder.geocode({'address': addr}, function (results, status) {
if (status === google.maps.GeocoderStatus.OK) {
if ($.isFunction(self.map.panTo) && undefined !== results[0]) {
self.map.panTo(results[0].geometry.location);
}
}
});
return;
} else {
if ('[object Array]' === Object.prototype.toString.call(addr)) {
if (2 === addr.length) {
latlng = new google.maps.LatLng(addr[0], addr[1]);
}
} else if (_hasOwnProperty(addr, 'lat') && _hasOwnProperty(addr, 'lng')) {
latlng = new google.maps.LatLng(addr.lat, addr.lng);
} else if (_hasOwnProperty(addr, 'x') && _hasOwnProperty(addr, 'y')) {
latlng = new google.maps.LatLng(addr.x, addr.y);
}
if ($.isFunction(self.map.panTo) && undefined !== latlng) {
self.map.panTo(latlng);
}
}
}
}
},
/**
* Method: Google Maps clear the specificed layer
* @param {string} type Layer type (markers, polylines, polygons, circles, kmls, directions)
* @public
*/
clear: function (layer) {
var self = this, layers, label, i, j;
if ('string' === typeof layer) {
layers = layer.split(',');
}
for (i = 0; i < layers.length; i += 1) {
label = '_' + $.trim(layers[i].toString().toLowerCase()) + 's';
if (undefined !== self[label] && self[label].length) {
for (j = 0; j < self[label].length; j += 1) {
self[label][j].set('visible', false);
self[label][j].setMap(null);
}
self[label] = [];
}
}
},
/**
* Method: Google Maps dynamic add layers
* @param {Object} options Refernce by tinyMap options
* @public
*/
modify: function (options) {
var self = this,
func = [],
label = [
['kml', 'kml'],
['marker', 'markers'],
['direction', 'direction'],
['polyline', 'DrawPolyline'],
['polygon', 'DrawPolygon'],
['circle', 'DrawCircle'],
['zoom', 'setZoom']
],
i;
if (undefined !== options) {
for (i = 0; i < label.length; i += 1) {
if (_hasOwnProperty(options, label[i][0])) {
func.push(label[i][1]);
}
}
if (null !== self.map && undefined !== self.map && func) {
for (i = 0; i < func.length; i += 1) {
if ('function' === typeof self[func[i]]) {
self[func[i]](self.map, options);
}
}
}
}
}
};
/**
* jQuery tinyMap instance
* @param {Object} options Plugin settings
* @public
*/
$.fn[pluginName] = function (options) {
var args = arguments, result, instance;
if ('string' === typeof options) {
this.each(function () {
instance = $.data(this, pluginName);
if (instance instanceof tinyMap && 'function' === typeof instance[options]) {
result = instance[options].apply(instance, Array.prototype.slice.call(args, 1));
}
});
return undefined !== result ? result : this;;
} else {
return this.each(function () {
if (!$.data(this, pluginName)) {
$.data(this, pluginName, new tinyMap(this, options));
}
});
}
};
//#!#END
})(jQuery, window, document);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment