/**
* @fileoverview 百度地图的城市列表类,对外开放。
* 帮助用户直接生成城市列表,并自定义点击城市的操作。
* 使用者可以通过接口直接获取城市数据。
* 主入口类是CityList,
* 基于Baidu Map API 1.5。
*
* @author Baidu Map Api Group
* @version 1.5
*/
/**
* @namespace BMap的所有library类均放在BMapLib命名空间下
*/
var BMapLib = window.BMapLib = BMapLib || {};
(function() {
/**
* 声明baidu包
*/
var baidu = baidu || {guid : "$BAIDU$"};
(function() {
// 一些页面级别唯一的属性,需要挂载在window[baidu.guid]上
window[baidu.guid] = {};
/**
* 将源对象的所有属性拷贝到目标对象中
* @name baidu.extend
* @function
* @grammar baidu.extend(target, source)
* @param {Object} target 目标对象
* @param {Object} source 源对象
* @returns {Object} 目标对象
*/
baidu.extend = function (target, source) {
for (var p in source) {
if (source.hasOwnProperty(p)) {
target[p] = source[p];
}
}
return target;
};
/**
* @ignore
* @namespace
* @baidu.lang 对语言层面的封装,包括类型判断、模块扩展、继承基类以及对象自定义事件的支持。
* @property guid 对象的唯一标识
*/
baidu.lang = baidu.lang || {};
/**
* 返回一个当前页面的唯一标识字符串。
* @function
* @grammar baidu.lang.guid()
* @returns {String} 当前页面的唯一标识字符串
*/
baidu.lang.guid = function() {
return "TANGRAM__" + (window[baidu.guid]._counter ++).toString(36);
};
window[baidu.guid]._counter = window[baidu.guid]._counter || 1;
/**
* 所有类的实例的容器
* key为每个实例的guid
*/
window[baidu.guid]._instances = window[baidu.guid]._instances || {};
/**
* Tangram继承机制提供的一个基类,用户可以通过继承baidu.lang.Class来获取它的属性及方法。
* @function
* @name baidu.lang.Class
* @grammar baidu.lang.Class(guid)
* @param {string} guid 对象的唯一标识
* @meta standard
* @remark baidu.lang.Class和它的子类的实例均包含一个全局唯一的标识guid。
* guid是在构造函数中生成的,因此,继承自baidu.lang.Class的类应该直接或者间接调用它的构造函数。
* baidu.lang.Class的构造函数中产生guid的方式可以保证guid的唯一性,及每个实例都有一个全局唯一的guid。
*/
baidu.lang.Class = function(guid) {
this.guid = guid || baidu.lang.guid();
window[baidu.guid]._instances[this.guid] = this;
};
window[baidu.guid]._instances = window[baidu.guid]._instances || {};
/**
* 判断目标参数是否string类型或String对象
* @name baidu.lang.isString
* @function
* @grammar baidu.lang.isString(source)
* @param {Any} source 目标参数
* @shortcut isString
* @meta standard
*
* @returns {boolean} 类型判断结果
*/
baidu.lang.isString = function (source) {
return '[object String]' == Object.prototype.toString.call(source);
};
/**
* 判断目标参数是否为function或Function实例
* @name baidu.lang.isFunction
* @function
* @grammar baidu.lang.isFunction(source)
* @param {Any} source 目标参数
* @returns {boolean} 类型判断结果
*/
baidu.lang.isFunction = function (source) {
return '[object Function]' == Object.prototype.toString.call(source);
};
/**
* 重载了默认的toString方法,使得返回信息更加准确一些。
* @return {string} 对象的String表示形式
*/
baidu.lang.Class.prototype.toString = function(){
return "[object " + (this._className || "Object" ) + "]";
};
/**
* 自定义的事件对象。
* @function
* @name baidu.lang.Event
* @grammar baidu.lang.Event(type[, target])
* @param {string} type 事件类型名称。为了方便区分事件和一个普通的方法,事件类型名称必须以"on"(小写)开头。
* @param {Object} [target]触发事件的对象
* @meta standard
* @remark 引入该模块,会自动为Class引入3个事件扩展方法:addEventListener、removeEventListener和dispatchEvent。
* @see baidu.lang.Class
*/
baidu.lang.Event = function (type, target) {
this.type = type;
this.returnValue = true;
this.target = target || null;
this.currentTarget = null;
};
/**
* 注册对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
* @grammar obj.addEventListener(type, handler[, key])
* @param {string} type 自定义事件的名称
* @param {Function} handler 自定义事件被触发时应该调用的回调函数
* @param {string} [key] 为事件监听函数指定的名称,可在移除时使用。如果不提供,方法会默认为它生成一个全局唯一的key。
* @remark 事件类型区分大小写。如果自定义事件名称不是以小写"on"开头,该方法会给它加上"on"再进行判断,即"click"和"onclick"会被认为是同一种事件。
*/
baidu.lang.Class.prototype.addEventListener = function (type, handler, key) {
if (!baidu.lang.isFunction(handler)) {
return;
}
!this.__listeners && (this.__listeners = {});
var t = this.__listeners, id;
if (typeof key == "string" && key) {
if (/[^\w\-]/.test(key)) {
throw("nonstandard key:" + key);
} else {
handler.hashCode = key;
id = key;
}
}
type.indexOf("on") != 0 && (type = "on" + type);
typeof t[type] != "object" && (t[type] = {});
id = id || baidu.lang.guid();
handler.hashCode = id;
t[type][id] = handler;
};
/**
* 移除对象的事件监听器。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
* @grammar obj.removeEventListener(type, handler)
* @param {string} type 事件类型
* @param {Function|string} handler 要移除的事件监听函数或者监听函数的key
* @remark 如果第二个参数handler没有被绑定到对应的自定义事件中,什么也不做。
*/
baidu.lang.Class.prototype.removeEventListener = function (type, handler) {
if (baidu.lang.isFunction(handler)) {
handler = handler.hashCode;
} else if (!baidu.lang.isString(handler)) {
return;
}
!this.__listeners && (this.__listeners = {});
type.indexOf("on") != 0 && (type = "on" + type);
var t = this.__listeners;
if (!t[type]) {
return;
}
t[type][handler] && delete t[type][handler];
};
/**
* 派发自定义事件,使得绑定到自定义事件上面的函数都会被执行。引入baidu.lang.Event后,Class的子类实例才会获得该方法。
* @grammar obj.dispatchEvent(event, options)
* @param {baidu.lang.Event|String} event Event对象,或事件名称(1.1.1起支持)
* @param {Object} options 扩展参数,所含属性键值会扩展到Event对象上(1.2起支持)
* @remark 处理会调用通过addEventListenr绑定的自定义事件回调函数之外,还会调用直接绑定到对象上面的自定义事件。
* 例如:
* myobj.onMyEvent = function(){}
* myobj.addEventListener("onMyEvent", function(){});
*/
baidu.lang.Class.prototype.dispatchEvent = function (event, options) {
if (baidu.lang.isString(event)) {
event = new baidu.lang.Event(event);
}
!this.__listeners && (this.__listeners = {});
options = options || {};
for (var i in options) {
event[i] = options[i];
}
var i, t = this.__listeners, p = event.type;
event.target = event.target || this;
event.currentTarget = this;
p.indexOf("on") != 0 && (p = "on" + p);
baidu.lang.isFunction(this[p]) && this[p].apply(this, arguments);
if (typeof t[p] == "object") {
for (i in t[p]) {
t[p][i].apply(this, arguments);
}
}
return event.returnValue;
};
/**
* 为类型构造器建立继承关系
* @name baidu.lang.inherits
* @function
* @grammar baidu.lang.inherits(subClass, superClass[, className])
* @param {Function} subClass 子类构造器
* @param {Function} superClass 父类构造器
* @param {string} className 类名标识
* @remark 使subClass继承superClass的prototype,
* 因此subClass的实例能够使用superClass的prototype中定义的所有属性和方法。
* 这个函数实际上是建立了subClass和superClass的原型链集成,并对subClass进行了constructor修正。
* 注意:如果要继承构造函数,需要在subClass里面call一下,具体见下面的demo例子
* @shortcut inherits
* @meta standard
* @see baidu.lang.Class
*/
baidu.lang.inherits = function (subClass, superClass, className) {
var key, proto,
selfProps = subClass.prototype,
clazz = new Function();
clazz.prototype = superClass.prototype;
proto = subClass.prototype = new clazz();
for (key in selfProps) {
proto[key] = selfProps[key];
}
subClass.prototype.constructor = subClass;
subClass.superClass = superClass.prototype;
if ("string" == typeof className) {
proto._className = className;
}
};
/**
* @ignore
* @namespace baidu.dom 操作dom的方法。
*/
baidu.dom = baidu.dom || {};
/**
* 从文档中获取指定的DOM元素
* @name baidu.dom.g
* @function
* @grammar baidu.dom.g(id)
* @param {string|HTMLElement} id 元素的id或DOM元素
* @meta standard
*
* @returns {HTMLElement|null} 获取的元素,查找不到时返回null,如果参数不合法,直接返回参数
*/
baidu.g = baidu.dom.g = function (id) {
if ('string' == typeof id || id instanceof String) {
return document.getElementById(id);
} else if (id && id.nodeName && (id.nodeType == 1 || id.nodeType == 9)) {
return id;
}
return null;
};
/**
* @ignore
* @namespace baidu.browser 判断浏览器类型和特性的属性。
*/
baidu.browser = baidu.browser || {};
if (/msie (\d+\.\d)/i.test(navigator.userAgent)) {
//IE 8下,以documentMode为准
/**
* 判断是否为ie浏览器
* @property ie ie版本号
* @grammar baidu.browser.ie
* @meta standard
* @shortcut ie
* @see baidu.browser.firefox,baidu.browser.safari,baidu.browser.opera,baidu.browser.chrome,baidu.browser.maxthon
*/
baidu.browser.ie = baidu.ie = document.documentMode || + RegExp['\x241'];
}
/**
* 提供给setAttr与getAttr方法作名称转换使用
* ie6,7下class要转换成className
* @meta standard
*/
baidu.dom._NAME_ATTRS = (function () {
var result = {
'cellpadding': 'cellPadding',
'cellspacing': 'cellSpacing',
'colspan': 'colSpan',
'rowspan': 'rowSpan',
'valign': 'vAlign',
'usemap': 'useMap',
'frameborder': 'frameBorder'
};
if (baidu.browser.ie < 8) {
result['for'] = 'htmlFor';
result['class'] = 'className';
} else {
result['htmlFor'] = 'for';
result['className'] = 'class';
}
return result;
})();
/**
* 获取目标元素的属性值
* @name baidu.dom.getAttr
* @function
* @grammar baidu.dom.getAttr(element, key)
* @param {HTMLElement|string} element 目标元素或目标元素的id
* @param {string} key 要获取的attribute键名
* @shortcut getAttr
* @meta standard
* @see baidu.dom.setAttr,baidu.dom.setAttrs
*
* @returns {string|null} 目标元素的attribute值,获取不到时返回null
*/
baidu.getAttr = baidu.dom.getAttr = function (element, key) {
element = baidu.dom.g(element);
if ('style' == key){
return element.style.cssText;
}
key = baidu.dom._NAME_ATTRS[key] || key;
return element.getAttribute(key);
};
/**
* @ignore
* @namespace baidu.event 屏蔽浏览器差异性的事件封装。
* @property target 事件的触发元素
* @property pageX 鼠标事件的鼠标x坐标
* @property pageY 鼠标事件的鼠标y坐标
* @property keyCode 键盘事件的键值
*/
baidu.event = baidu.event || {};
/**
* 事件监听器的存储表
* @private
* @meta standard
*/
baidu.event._listeners = baidu.event._listeners || [];
/**
* 为目标元素添加事件监听器
* @name baidu.event.on
* @function
* @grammar baidu.event.on(element, type, listener)
* @param {HTMLElement|string|window} element 目标元素或目标元素id
* @param {string} type 事件类型
* @param {Function} listener 需要添加的监听器
* @remark
* 1. 不支持跨浏览器的鼠标滚轮事件监听器添加
* 2. 改方法不为监听器灌入事件对象,以防止跨iframe事件挂载的事件对象获取失败
* @shortcut on
* @meta standard
* @see baidu.event.un
*
* @returns {HTMLElement|window} 目标元素
*/
baidu.on = baidu.event.on = function (element, type, listener) {
type = type.replace(/^on/i, '');
element = baidu.g(element);
var realListener = function (ev) {
// 1. 这里不支持EventArgument, 原因是跨frame的事件挂载
// 2. element是为了修正this
listener.call(element, ev);
},
lis = baidu.event._listeners,
filter = baidu.event._eventFilter,
afterFilter,
realType = type;
type = type.toLowerCase();
// filter过滤
if(filter && filter[type]){
afterFilter = filter[type](element, type, realListener);
realType = afterFilter.type;
realListener = afterFilter.listener;
}
// 事件监听器挂载
if (element.addEventListener) {
element.addEventListener(realType, realListener, false);
} else if (element.attachEvent) {
element.attachEvent('on' + realType, realListener);
}
// 将监听器存储到数组中
lis[lis.length] = [element, type, listener, realListener, realType];
return element;
};
})();
/**
* @fileoverview 数据管理模块.
*/
var DataMgr = {
/**
* 请求函数
* @param String 地址.
* @param Function 回调函数.
*/
request: function(url, cbk) {
if (cbk) {
// 生成随机数
var timeStamp = (Math.random() * 100000).toFixed(0);
// 全局回调函数
BMap["_rd"]["_cbk" + timeStamp] = function(json){
cbk && cbk(json);
delete BMap["_rd"]["_cbk" + timeStamp];
};
url += "&callback=BMap._rd._cbk" + timeStamp;
}
var script = document.createElement('script');
script.setAttribute('src', url);
document.body.appendChild(script);
// 脚本加载完成后进行移除
if (script.addEventListener) {
script.addEventListener('load', function(e) {
var t = e.target;
t.parentNode.removeChild(t);
}, false);
}
else if (script.attachEvent) {
script.attachEvent('onreadystatechange', function(e) {
var t = window.event.srcElement;
if (t && (t.readyState == 'loaded' || t.readyState == 'complete')) {
t.parentNode.removeChild(t);
}
});
}
// 使用setTimeout解决ie6无法发送问题
setTimeout(function() {
document.getElementsByTagName('head')[0].appendChild(script);
script = null;
}, 1);
}
};
var config = {
serviceUrl : "http://api.map.baidu.com"
}
function geoToPoint(geo) {
var projection = BMAP_NORMAL_MAP.getProjection();
var point = null;
geo = geo.split('|')[2];
geo = geo.substr(0, geo.length - 1);
geo = geo.split(',');
var point = projection.pointToLngLat(new BMap.Pixel(geo[0], geo[1]));
return point;
}
function coordinateToPoints(coordinate) {
var projection = BMAP_NORMAL_MAP.getProjection();
var points = [];
coordinate = coordinate.split(';');
for (var i = 0, len = coordinate.length; i < len; i++) {
var pos = coordinate[i].split(',');
var point = projection.pointToLngLat(new BMap.Pixel(pos[0], pos[1]));
points.push(point);
}
return points;
}
/**
* @exports CityList as BMapLib.CityList
*/
var CityList =
/**
* CityList类的构造函数
* @class 城市列表类,
* 实例化该类后,可以帮助用户直接生成城市列表,
* 也可以通过接口获取城市数据。
*
* @constructor
* @param {Json Object} opts 可选的输入参数,非必填项。可输入选项包括:
* {"container" : {String|HTMLElement} 需要提供界面方式展现的容器。如果此参数为空,则不提供界面方式,也没有cityclick的事件派发
*
"map" : {BMap} 实例化的map对象,如果传入此参数,则用户点击界面中的城市时,可以直接帮助用户定位到地图的相关城市位置}
*
* @example 参考示例:
* var myCityListObject = new BMapLib.CityList({container : "container"});
*/
BMapLib.CityList = function(opts){
opts = opts || {};
/**
* _opts是默认参数赋值。
* 下面通过用户输入的opts,对默认参数赋值
* @private
* @type {Json}
*/
this._opts = baidu.extend(
baidu.extend(this._opts || {}, {
/**
* 提供界面方式展现的容器
* @private
* @type {String|HTMLElement}
*/
container : null,
/**
* 实例化的BMap对象
* @private
* @type {BMap}
*/
map : null
})
, opts);
/**
* 城市数据的存储
* @private
* @type {Json}
*/
this._data = null;
this._init();
}
// 通过baidu.lang下的inherits方法,让CityList继承baidu.lang.Class
baidu.lang.inherits(CityList, baidu.lang.Class, "CityList");
CityList.prototype._init = function () {
//if (this._opts.container && !$('#'+this._opts.container).html()){
this._initContainer();
//}
}
/**
* 获取商圈数据
* @param {String} 商圈名称
* @param {Function} 回调函数,结果在回调函数中回传
* [
* {
* city: "北京市", //商圈所在城市名
* coordinate: {Array}, //商圈所在的坐标范围,Point数组
* district: "海淀区", //商圈所在的区域
* type: "4-优质商圈" //商圈的类型
* }
* ]
*
*/
CityList.prototype.getBusiness = function (business, cbk) {
var url = config.serviceUrl + "/shangquan/reverse/?wd=" + business;
DataMgr.request(url,function(json){
var result = null;
if (json && json['error'] && json['error']['errno'] == "0") {
result = json['content'];
}
for (var i = 0, len = result.length; i < len; i++) {
result[i]['coordinate'] = coordinateToPoints(result[i]['coordinate']);
}
console.log(result);
if (cbk) {
cbk(result);
}
});
}
/**
* 获取下级的区域列表
* @param {String} 城市代码(cityCode),参考百度地图城市名称-城市代码(cityCode)关系对照:http://developer.baidu.com/map/devRes.htm
* @param {Function} 回调函数,结果在回调函数中回传
* 返回的json结果代表的意思
* {
* area_code: 131, //城市区域code
* area_name: "北京市", //城市区域名称
* area_type: 2, //城市区域类型
* geo: {Point}, //城市区域中心点
* sup_business_area: 0 ,//是否存在商圈,仅在区的级别(area_type=3)才会有此字段
* sub: {Array} //下级区域列表, 里面内容同上面的那些字段
* }
*/
CityList.prototype.getSubAreaList = function (areaCode, cbk) {
var url = config.serviceUrl + "/shangquan/forward/?qt=sub_area_list&ext=1&level=1&areacode=" + areaCode + "&business_flag=0";
DataMgr.request(url,function(json){
var result = null;
if (json && json['result'] && json['result']['error'] == "0") {
result = json['content'];
}
result.geo = geoToPoint(result.geo);
for (var i = 0, len = result.sub.length; i < len; i++) {
result.sub[i]['geo'] = geoToPoint(result.sub[i]['geo']);
}
console.log(result);
if (cbk) {
cbk(result);
}
});
}
CityList.prototype._initContainer = function () {
var me = this;
/*请城市地区*/
function selectArea(areacode, id, level, isbusiness){
selectArea.id = id;
selectArea.level = level;
var t= new Date().getTime();
var url = AreaUrl + "&areacode=" + areacode;
if (isbusiness) {
url += "&business_flag=1";
} else {
url += "&business_flag=0";
}
DataMgr.request(url,getAreaData);
}
function getAreaData(data){
var subList = "";//城区列表
if(data.content){
var c = data.content;
var html = [];
var geo = execGeo(c.geo);
var mp = new BMap.MercatorProjection();
var po = mp.pointToLngLat(new BMap.Pixel(geo.x, geo.y))
if(data.content.area_code == 1){
var point = new BMap.Point(121.455129,31.229402);
map.centerAndZoom(point, 12);
}else{
var point = new BMap.Point(po.lng, po.lat);
map.centerAndZoom(point, selectArea.level);
}
/*渲染城市*/
if(data.content.sub){
subList = c.sub;
subList.splice(0,0,{area_name:'请选择', area_code:''});
var fragment = document.createDocumentFragment();
var o;
var citycode = {131: 1, 289: 2, 332: 3, 132: 4};
for(var i=0; i{"area_name : {String} 点击的区域名称,
*
{"area_code : {String} 点击的区域代码,
*
"geo:{BMap.Point} 点击区域合适显示的中心点位置,
*
"area_type:{Number} 该区域的类型(全国0、省1、城市2)
*
* @example 参考示例:
* myCityListObject.addEventListener("cityclick", function(e) { alert(e.area_name); });
*/
me.dispatchEvent('cityclick', {
area_code: option.value,
area_type: option.area_type,
geo: geoToPoint(option.geo),
area_name: option.title
});
}
baidu.on(businessDom, "change", function(e){
storage.removeItem('business');
storage.removeItem('points');
//localStorage.business=businessDom.options[businessDom.selectedIndex].text;//商圈
storage.setItem('business',businessDom.options[businessDom.selectedIndex].text);
var s_value = businessDom.value;
var option = this.options[this.selectedIndex];
var geo = execGeo(option.geo);
var mp = new BMap.MercatorProjection();
var po = mp.pointToLngLat(new BMap.Pixel(geo.x, geo.y));
var business_geo = option.business_geo;
business_geo = business_geo.split(';');
for (var i = 0,len = business_geo.length; i < len; i++) {
var business_p = business_geo[i].split(',');
business_geo[i] = mp.pointToLngLat(new BMap.Pixel(business_p[0], business_p[1]))
}
var points=new Array();
for (i in business_geo){
points[i] = [business_geo[i].lng,business_geo[i].lat];
}
storage.setItem('points',points);//商圈
map.clearOverlays();
var polygon = new BMap.Polygon();
map.addOverlay(polygon);
polygon.show();
polygon.setPath(business_geo);
map.setViewport(business_geo);
dispatchCityClick(this.options[this.selectedIndex]);
//var point = new BMap.Point(po.lng, po.lat);
//map.centerAndZoom(point, 14);
});
selectArea(1, provinceDom, 12);
}
})();