公司业务里一直都有使用leaflet地图插件来做地图展示、绘图等操作。公司有个项目已经有好几年了,由于项目原因一直在使用,今年由于google 地图 api过期,导致已经使用的地图无法加载。我作为现在该项目的维护者,虽然未参与项目的开发阶段,但是事情在我手上了还是要处理的。
上面说到api过期,无法加载地图了。我首先想到的是更换api key,翻墙出去,搞了许久,没有成功。想想也是,就我这个英语水平成功就是偶然。当然也不全是英语问题,在我在墙外的世界捣鼓来捣鼓去的时候,隐约的发现谷歌api 有关的认证加强了(事情过了1一个月了,也忘记了)。
在最简单的方法尝试无效后,下定决心重写吧。好,写,一顿搜索猛如虎(差点写成:猛如狗了^_^),最后找了个现成的代码来改吧改吧就可用了。其实改完效果还可以,比以前多了 图层切换功能,而且使用国内地图也不怕哪天项目上说用谷歌不行了。
上面的事情距离我现在写这个文章的时候有一个月了。我是按照用一个项目改一个项目的原则,有些项目无人问津了,我便没改了。按照之前的套路,合并了其他已改项目的分支,项目类似,基本合并无冲突。代码到了现在的项目上了,由于这个项目用到了这套系统且要更新功能,我真nm的欲哭无泪,好老的项目了(用的还是php yii1的框架,前端用的是一框bjui的框架,说起它可能知道的人不多,但是jui框架应该要多点,它就是基于jui的框架)。
埋怨项目老,技术老之后还是要做事,因为在系统升级完成之前必须用它苟延残喘下去。翻看着代码,终于忍不住了,怎么可以这么重复的做某件事呢,难道是对它不想忘记吗? 重复不说,js里面混入php的判断,这些老的套路,越看越那啥了
又吐槽了代码之后,我先来展示一下那些 旋转代码吧(╯﹏╰)
就这样的代码,能在一个代码里面好几个地方重复出现,你说是有多大的心去维护啊,又难受了。
说了这么的故事,该说重点了,基于上面的情况呢,必须有个什么东西来优化它,想来想去,考虑地图是基于dom生成加载的,那这样的话,写一个jquery的插件应该是比较好的了,当然的话自己封装一个类也行。
对于jquery插件的话,我分享一个网址吧:jquery插件
附上巨人的肩膀
1、ChineseTmsProviders:Leaflet.ChineseTmsProviders
2、tileLayer.baidu
再谈谈我心路吧,其实也是司空见惯的东西了。
- 为了更好的配置,我选择了两种配置,也就是除了自己传递配置参数的话,也可以通过标签属性传递
- 不好解决坐标系问题,我选择了加一个参数来区分百度和其它地图
- 外部更好的使用,必须在需要的地方添加回调参数
- 为了统一的管理和不重复的添加,我选择了使用一个变量存储所有加载的图层。在设置的时候,已经出现的图层会直接返回
最后我贴上目前的一个代码,改代码下午才写,不完善,下周应该会完善自己需要使用的地方。虽然已过了青葱的岁月,可是代码水平还是难^_^。
/** * jQuery and leaflet Plugin createMap * jquery插件:生成百度、谷歌、高德、天地图等地图到页面 * * version 1.0, 2020-03-20 * by jcy * Git repository : https://github.com/jiechengyang/jqLeafletCreateMap */ /** * example: * * 默认生成谷歌等多图层【查看地图右上角图层管理工具】:var mapService = $("#map").createMap({center: [lat, lng]}); * * 生成百度地图:var mapService = $("#map").createMap({center: [lat, lng], imap: 'Bd.Normal.Map'}); * * 生成单张地图请传递:single:true * * 修改默认加载地图请传:iLayer:'对应chinaProviderKeys的key值' * */ /** * add method: * iconMarker: 图标加载 * addPolygon: 面加载 * */ (function ($) { function arrIndexOf(array, find, field) { var index = -1; for (var i = 0; i < array.length; i++) { var arr = array[i]; if (arr[field] === find) { index = i; break; } } return index; } function mapService() { this.layers = []; this.chinaProviderKeys = [ { key: 'Geoq.Normal.Map', name: '智图地图', group: 'Geog' }, { key: 'Geoq.Normal.PurplishBlue', name: '智图午夜蓝', group: 'Geog' }, { key: 'Geoq.Normal.Gray', name: '智图灰色', group: 'Geog' }, { key: 'Geoq.Normal.Warm', name: '智图暖色', group: 'Geog' }, { key: 'TianDiTu.Normal.Map', name: '天地图', group: 'TianDiTu' }, { key: 'TianDiTu.Normal.Annotion', name: '', group: 'TianDiTu' }, { key: 'TianDiTu.Satellite.Map', name: '天地图影像', group: 'TianDiTu' }, { key: 'TianDiTu.Satellite.Annotion', name: '', group: 'TianDiTu' }, { key: 'Google.Normal.Map', name: '谷歌地图', group: 'Google' }, { key: 'Google.Satellite.Map', name: '谷歌影像', group: 'Google' }, { key: 'GaoDe.Normal.Map', name: '高德地图', group: 'GaoDe' }, { key: 'GaoDe.Satellite.Map', name: '高德影像', group: 'GaoDe' }, { key: 'GaoDe.Satellite.Annotion', name: '', group: 'GaoDe' } ]; this.map = null; this.get = function (key) { return this.layers.indexOf(key) === 1 ? this.layers[key] : null; } this.set = function (key, options) { if (this.layers.indexOf(key) >= 0) { return this.get(key); } if (arrIndexOf(this.chinaProviderKeys, key, 'key') === -1) { return null; } var layer = L.tileLayer.chinaProvider(key, options); this.layers[key] = layer; return layer; } this.delete = function (key) { delete this.layers[key]; } this.getMap = function () { return this.map; } this.destroy = function () { this.layers = []; this.map = null; } this.init = function (domId, settings) { if (!settings.hasOwnProperty('iLayer')) { settings.iLayer = settings.imap; } var defaultMapOpts = { zoom: settings.zoom, zoomControl: settings.zoomControl, center: settings.center }; var bdMap = false; if (settings.imap === 'Bd.Normal.Map') { defaultMapOpts.crs = L.CRS.Baidu; bdMap = true; } var mainMap = L.map(domId, defaultMapOpts); this.map = mainMap; var opts = { maxZoom: settings.maxZoom, minZoom: settings.minZoom, }; if (bdMap) { opts.layer = settings.bdLayer ? settings.bdLayer : 'custom'; opts.customid = settings.bdCustomid ? settings.bdCustomid : 'googlelite'; var firstLayer = this.getBdMap(opts); settings.single = true; } else { var firstLayer = this.set(settings.iLayer, opts); } if (firstLayer) { mainMap.addLayer(firstLayer); } if (!settings.single) { var baseLayers = this.addLayers(settings.method, opts); if (baseLayers) { var providerName = this.chinaProviderKeys[arrIndexOf(this.chinaProviderKeys, settings.iLayer, 'key')]['name']; baseLayers[providerName] = firstLayer; L.control.layers(baseLayers, null).addTo(mainMap); } } L.Util.requestAnimFrame(mainMap.invalidateSize, mainMap, !1, mainMap._container); } this.addLayers = function (method, opts) { method = 'add' + method; if (this.hasOwnProperty(method)) { return this[method].call(this, opts); } return {}; } /** * 智图内容 */ this.addGeoNorMaps = function (options) { var layers = {}; for (var i = 0; i < this.chinaProviderKeys.length; i++) { var provider = this.chinaProviderKeys[i]; if (provider.group !== 'Geog') continue; var layer = this.set(provider.key, options); if (layer) { layers[provider.name] = layer; } } return layers; } /** * 百度地图 */ this.getBdMap = function (options) { options = $.extend({ // maxZoom: 18, // minZoom: 4, attribution: 'ⓒ 2020 Bd Map', layer: 'custom', customid: 'grassgreen', // crs: L.CRS.Baidu }, options); //百度:titleLayer.baidu.js //TODO: 请引入 proj4.js 和 proj4leaflet.js var layer = new L.tileLayer.baidu(options); this.layers['Bd.Normal.Map'] = layer; return layer; } /** * 天地图内容 */ this.addTianDiMaps = function (options) { var normalm = this.set('TianDiTu.Normal.Map', options), normala = this.set('TianDiTu.Satellite.Annotion', options), imgm = this.set('TianDiTu.Satellite.Map', options), imga = this.set('TianDiTu.Normal.Map', options); var normal = L.layerGroup([normalm, normala]), image = L.layerGroup([imgm, imga]); return { "天地图": normal, "天地图影像": image, } } /** * 谷歌地图内容 */ this.addGoogleMaps = function (options) { var normalMap = this.set('Google.Normal.Map', options), satelliteMap = this.set('Google.Satellite.Map', options); return { "谷歌地图": normalMap, "谷歌影像": satelliteMap, } } /** * 高德地图 */ this.addGaoDeMaps = function (options) { var Gaode = this.set('GaoDe.Normal.Map', options); var Gaodimgem = this.set('GaoDe.Satellite.Map', options); var Gaodimga = this.set('GaoDe.Satellite.Annotion', options); var Gaodimage = L.layerGroup([Gaodimgem, Gaodimga]); return { "高德地图": Gaode, "高德影像": Gaodimage, } } /** * 所有地图 */ this.addAllMaps = function (options) { var keys = [ 'GeoNorMaps', 'TianDiMaps', 'GoogleMaps', 'GaoDeMaps', ]; var layers = {}; for (var i in keys) { var method = 'add' + keys[i]; if (this.hasOwnProperty(method)) { layers = $.extend(layers, this[method](options)); } } return layers; } } /** * * 图表标记 */ mapService.prototype.iconMarker = function (icon, marker, labelContent, popupOpts, events) { events = events || null; labelContent = labelContent || null; var defaultOpts = { minWidth: 500, className: 'leaflet-popup' }; popupOpts = $.extend(defaultOpts, popupOpts); var marker = new L.Marker(marker, { icon: icon }); this.getMap().addLayer(marker); if (labelContent) { var label = new L.Label({ noHide: true }); label.setContent(labelContent).setLatLng({lat: marker.lat, lng: marker.lng + 1.4}); this.getMap().showLabel(label); } if (popupOpts.id) { marker.bindPopup(document.getElementById(popupOpts.popupId), popupOpts); } if (events) { for (var eKey in events) { marker.on(eKey, events[eKey]); } } return marker; }; /** * * 多边形地块 */ mapService.prototype.landPolygon = function (polygonObj, opts, events) { var defaultOpts = { initColor: 'pink', initOpacity: 0.3, hoverColor: 'orange', hoverOpacity: 0.3, domId: 'leaflet_land_popup_' + polygonObj.id, }; events = events || {}; opts = $.extend(defaultOpts, opts); if (polygonObj.hasWarning) { opts.initColor = 'red'; } var land = new L.Polygon(polygonObj.polygon, { weight: 2, opacity: opts.initOpacity, fillOpacity: opts.initOpacity, color: opts.initColor }); this.getMap().addLayer(land); var label = new L.Label(); label.setContent(polygonObj.labelContent).setLatLng(land.getBounds().getCenter()); this.getMap().showLabel(label); var defaultEvent = { mouseover: function (event) { land.setStyle({ color: opts.hoverColor, opacity: opts.hoverOpacity, fillOpacity: opts.hoverOpacity, }); }, mouseout: function (event) { land.setStyle({ color: opts.initColor, opacity: opts.initOpacity, fillOpacity: opts.initOpacity, }); } }; events = $.extend(defaultEvent, events); for (var eKey in events) { land.on(eKey, events[eKey]); } if (polygonObj.isBindPopup) { land.bindPopup(document.getElementById(polygonObj.domId), { minWidth: 500, className: 'leaflet-popup' }); } return land; }; var methods = { init: function (options) { var tData = this.data() || {}; // TODO: 参数合并:隐式参数与显示参数;隐式参数:dom标签的data-属性 显示参数:调用对象传参 options = $.extend(tData, options); var settings = $.extend({ maxZoom: 18,// leaflet 参数 minZoom: 5,// leaflet 参数 zoom: 14,// leaflet 参数 zoomControl: false,// leaflet 参数 center: [30.29898916168688, 103.57831210632321],// leaflet 参数 imap: 'Google.Normal.Map',// 插件 参数:默认地图加载谷歌地图,Bd.Normal.Map:由于坐标系问题,暂时只能加载它一个地图 bdLayer: 'custom',// 百度地图图层选择, bdCustomid: 'googlelite',// 插件 参数:百度地图图层样式选择 single: false,// 插件 参数:是否加载单个地图图层:默认是加载多张 method: 'AllMaps'// 插件 参数:加载多张地图图层时有几种类型选择:智图、天地图、百度、谷歌、高德 }, options); var service = new mapService(); service.init(this.attr('id'), settings); return service; }, destroy: function () { this.empty(); } }; $.fn.createMap = function (method) { method = method || null; if (methods[method]) { return methods[method].apply(this, Array.prototype.slice.call(arguments, 1)); } else if (typeof method === 'object' || !method) { return methods.init.apply(this, arguments); // return methods.init(this, methods); } else { $.error('Method' + method + 'does not exist on jQuery.tooltip'); } } })(jQuery);
最后再说一下,我们提升代码质量的唯一途径只有看源码。愿得到指点,愿共勉。
-------------------------垃圾猿