使用d3.js实现中国地图及中国地图下钻到省市区
在可视化开发中,地图是很重要的一个环节,很多时候需要展现的不仅是国家地图,还需要能从国家进入到省市,看到区这样的下钻过程,今天我们就来实现这个效果。
因为地图数据过于庞大,例子中删除了除北京外其他的地市信息。所以,请点击北京查看效果,其他省没效果。
最终效果
点击北京市查看效果,其他区域切换效果,空白区域返回全国。
http://en.jsrun.net/mviKp/show
代码解析
首先准备地图json数据
这里只展示部分数据,文章末尾会有完整数据
var chinajson = {"type": "FeatureCollection",
"features":
[
{"type":"Feature","properties":{"name":"伊宁县","id":"654021"},"geometry":{"type":"Polygon","coordinates":[[[81.4073450000001,43.8938430000001],[81.4195373828126,43.8904195380859],[81.4107684619142,43.8816506171876],[81.4073450000001,43.8938430000001],[81.4073450000001,43.8938430000001],[81.3917010791017,43.8981990791016],[81.3735931689455,43.921659772461],[81.451084013672,43.9432381416016],[81.431312895508,43.9584987617187],[81.4429889208986,43.9881990791016],[81.4473450000001,44.033843],[81.4595373828126,44.0372664619141],[81.4507684619142,44.0460353828125],[81.4473450000001,44.033843],[81.4140906054689,44.0210604072266],[81.3759973437501,44.043452375],[81.3057202441407,44.0233846259766],[81.2923450000001,44.0400868964844],[81.2828979785157,44.0282900214844],[81.2672113330079,44.0157283759766],[81.2428979785157,44.0272206855469],[81.2799335009767,44.0779213691406],[81.3062524707033,44.1699727607422],[81.3261676318361,44.2038521552735],[81.3928979785157,44.2182900214844],[81.447343779297,44.2338368964844],[81.4029291064455,44.2893709541016],[81.3973450000001,44.293843],[81.387437216798,44.3081966376953],[81.361429726563,44.317927726563],[81.3232605273438,44.3518575263672],[81.3644153125001,44.4150569892578],[81.3573450000001,44.443843],[81.3745306689454,44.4504488349609],[81.525142241211,44.4316109443359],[81.5751617724611,44.4173274970704],[81.6200805957033,44.4229146552735],[81.6317920214845,44.4082900214844],[81.6428979785157,44.3993959785156],[81.6527978808595,44.3870333076172],[81.7013489062501,44.3930727363282],[81.7117920214845,44.3682900214844],[81.7328979785158,44.3593959785156],[81.7417920214844,44.3482900214844],[81.7473450000001,44.343843],[81.7753821093751,44.3503395820313],[81.834785185547,44.3269869208985],[81.8527356250001,44.3296395087891],[81.879097319336,44.3137380195313],[82.006242097267,44.2921388984375],[82.0273450000001,44.283843],[82.0216192919923,44.2149562812501],[82.0665985400392,44.1867836738282],[82.061943388672,44.1288430000001],[82.0642138964845,44.1005825019532],[82.0402911669923,44.0668428779297],[82.020791044922,44.0020735908204],[81.9727167041017,43.9806221748047],[81.9819732958986,43.9684712958984],[82.0069043261719,43.946991803711],[82.0228534228517,43.888716046875],[82.007894926758,43.8619051337891],[82.0563116748048,43.8348915839844],[82.1625714404298,43.8492958808594],[82.1819732958985,43.8384712958985],[82.2135595996095,43.8289607978516],[82.2119195849611,43.8085469794922],[82.2412762744141,43.7671431708984],[82.2446264941408,43.7254488349609],[82.2313055712891,43.6767763496094],[82.2327630908204,43.6586397529297],[82.2219732958985,43.6192147041016],[82.2173450000001,43.5938430000001],[82.1925012500001,43.5993740058594],[82.0921887500002,43.5883119941407],[82.0393762500001,43.6000691962891],[82.0074731738282,43.5968172431641],[81.9625604541016,43.609374616211],[81.9427350146485,43.6073537421876],[81.9328076464845,43.6193056464844],[81.8538720996095,43.6333718085938],[81.7559155566408,43.6152565742188],[81.7173450000001,43.6238430000001],[81.7127167041017,43.6392147041016],[81.6594824511719,43.6629689765625],[81.5714526660158,43.7274678779297],[81.5457245166017,43.7573311591798],[81.4565002734376,43.8132161689454],[81.4427167041017,43.8292147041016],[81.4373450000001,43.833843],[81.4617010791017,43.8694869208985],[81.4913800341798,43.8923946357422],[81.4623450000001,43.8881038642579],[81.4523450000001,43.8895821357422],[81.4312549121095,43.8864650703125],[81.4345502001954,43.9087654853516],[81.4141272265626,43.9181990791016],[81.4073450000001,43.8938430000001]]]}},
{"type": "Feature","properties":{"id":"110228","name":"密云县","cp":[117.0923,40.5121],"childNum":1},"geometry":{"type":"Polygon","coordinates":[[[116.7586,40.7064],[116.7847,40.7016],[116.7888,40.714],[116.7902,40.727],[116.7819,40.7524],[116.7833,40.7579],[116.7902,40.7517],[116.8025,40.7462],[116.819,40.7504],[116.8272,40.749],[116.8396,40.7607],[116.8341,40.7703],[116.8506,40.7751],[116.8506,40.7785],[116.8671,40.7847],[116.8629,40.7922],[116.8712,40.7943],[116.8739,40.7991],[116.8808,40.7977],[116.8863,40.8012],[116.8959,40.7936],[116.8945,40.7819],[116.8973,40.7778],[116.9234,40.7737],[116.9247,40.7627],[116.9289,40.7565],[116.9234,40.7524],[116.9261,40.7449],[116.9412,40.7401],[116.9453,40.7284],[116.9563,40.7229],[116.9659,40.714],[116.9646,40.7092],[117.0044,40.6968],[117.0195,40.6954],[117.0305,40.692],[117.0497,40.7002],[117.084,40.7023],[117.0964,40.7057],[117.1115,40.7071],[117.117,40.6995],[117.1225,40.7009],[117.1527,40.6968],[117.1664,40.6989],[117.1788,40.6934],[117.1829,40.6975],[117.2063,40.6947],[117.231,40.6844],[117.242,40.6769],[117.2612,40.681],[117.2777,40.6666],[117.2914,40.6597],[117.3257,40.6604],[117.3367,40.6631],[117.3367,40.6666],[117.3463,40.6741],[117.3615,40.6748],[117.3903,40.6837],[117.4068,40.6865],[117.4205,40.6865],[117.4507,40.6796],[117.4644,40.6734],[117.4796,40.6776],[117.4947,40.6748],[117.5056,40.6666],[117.507,40.6549],[117.5029,40.6528],[117.5015,40.6364],[117.4796,40.6357],[117.4713,40.6474],[117.448,40.6419],[117.4301,40.6405],[117.4219,40.6377],[117.4178,40.6185],[117.4136,40.6062],[117.4205,40.5945],[117.4232,40.5821],[117.4301,40.5787],[117.426,40.5732],[117.4205,40.5691],[117.404,40.5739],[117.4013,40.5698],[117.3944,40.567],[117.3889,40.5615],[117.3766,40.5663],[117.3656,40.5759],[117.3505,40.5801],[117.334,40.5766],[117.3106,40.5766],[117.2955,40.567],[117.2667,40.5581],[117.2502,40.5485],[117.253,40.5416],[117.2502,40.5375],[117.2598,40.5196],[117.2475,40.5121],[117.2392,40.5176],[117.2337,40.5135],[117.2131,40.5128],[117.2104,40.5073],[117.209,40.497],[117.2173,40.4949],[117.2282,40.4812],[117.2255,40.4778],[117.2365,40.4688],[117.2351,40.4578],[117.2639,40.4414],[117.2571,40.4297],[117.2502,40.429],[117.2337,40.4166],[117.2324,40.4036],[117.2392,40.3988],[117.2406,40.3947],[117.2365,40.394],[117.2351,40.3892],[117.2282,40.3857],[117.2241,40.3754],[117.2186,40.3775],[117.209,40.3734],[117.1925,40.3761],[117.1774,40.3748],[117.1637,40.3706],[117.172,40.372],[117.172,40.3672],[117.139,40.3624],[117.1184,40.3548],[117.1129,40.3542],[117.1005,40.3603],[117.0593,40.3377],[117.0538,40.337],[117.0525,40.3439],[117.0374,40.3473],[117.0236,40.337],[117.0126,40.326],[117.0113,40.3082],[117.0058,40.3027],[117.0003,40.302],[117.0044,40.2958],[116.9728,40.2848],[116.9522,40.2621],[116.9495,40.2567],[116.9659,40.2374],[116.9536,40.2292],[116.9412,40.2237],[116.9316,40.2306],[116.9206,40.223],[116.9028,40.2244],[116.8973,40.2319],[116.8918,40.2347],[116.8932,40.2415],[116.8739,40.2683],[116.8753,40.2759],[116.8712,40.2889],[116.8588,40.2917],[116.8492,40.3102],[116.8341,40.3082],[116.8272,40.2985],[116.8231,40.2862],[116.819,40.2834],[116.808,40.2869],[116.7888,40.2889],[116.7833,40.2793],[116.7696,40.2731],[116.7517,40.2752],[116.7448,40.2793],[116.7352,40.2793],[116.7435,40.2889],[116.7435,40.2944],[116.7668,40.3143],[116.7696,40.3219],[116.7682,40.3253],[116.7517,40.3267],[116.7503,40.3315],[116.7448,40.3349],[116.7448,40.3391],[116.7297,40.3349],[116.7311,40.3391],[116.7229,40.3397],[116.727,40.361],[116.7188,40.361],[116.7188,40.3693],[116.7133,40.3686],[116.7064,40.3761],[116.7119,40.3802],[116.7091,40.3871],[116.7119,40.3919],[116.7215,40.396],[116.7242,40.4036],[116.7242,40.4166],[116.7201,40.4235],[116.7188,40.4297],[116.7201,40.4386],[116.7229,40.4427],[116.7229,40.4475],[116.7078,40.4661],[116.7009,40.4812],[116.6927,40.4881],[116.6982,40.4942],[116.7009,40.508],[116.7119,40.5183],[116.7133,40.5231],[116.7174,40.5251],[116.7091,40.532],[116.7091,40.543],[116.6776,40.5519],[116.6844,40.554],[116.6982,40.5656],[116.7064,40.5643],[116.7146,40.5746],[116.7119,40.5849],[116.7091,40.5904],[116.7105,40.6007],[116.705,40.6027],[116.7078,40.6075],[116.7023,40.6151],[116.7064,40.624],[116.7023,40.6316],[116.7119,40.6425],[116.7119,40.6556],[116.7091,40.6618],[116.7146,40.668],[116.7146,40.6796],[116.7311,40.6913],[116.7476,40.6968],[116.7586,40.7064]]]}},
{"type": "Feature","properties":{"id":"110116","name":"怀柔区","cp":[116.6377,40.6219],"childNum":1},"geometry":{"type":"Polygon","coordinates":[[[116.5169,40.5773],[116.5224,40.5814],[116.532,40.5924],[116.5361,40.6041],[116.5334,40.6082],[116.5416,40.6268],[116.5636,40.6281],[116.5691,40.6247],[116.5732,40.6309],[116.5746,40.6364],[116.5649,40.6371],[116.5512,40.6425],[116.5416,40.6432],[116.5375,40.6535],[116.5279,40.6528],[116.5196,40.657],[116.5196,40.6645],[116.5128,40.6721],[116.4867,40.6748],[116.4839,40.6783],[116.488,40.6899],[116.5004,40.6968],[116.5004,40.705],[116.5045,40.7078],[116.5045,40.7201],[116.51,40.7249],[116.51,40.7311],[116.5141,40.7414],[116.5018,40.7469],[116.5004,40.76],[116.4922,40.7627],[116.4812,40.771],[116.4661,40.7723],[116.4592,40.797],[116.4523,40.7984],[116.4427,40.8073],[116.4386,40.8183],[116.4249,40.8231],[116.407,40.8334],[116.4056,40.841],[116.3974,40.852],[116.3919,40.8554],[116.3892,40.8623],[116.3823,40.8636],[116.3342,40.9055],[116.3342,40.92],[116.3411,40.9309],[116.3548,40.9364],[116.3589,40.9364],[116.3658,40.9433],[116.3713,40.9433],[116.3782,40.9364],[116.3919,40.9145],[116.4015,40.9055],[116.4153,40.9007],[116.4304,40.9035],[116.4372,40.8994],[116.4606,40.9],[116.4647,40.8959],[116.4743,40.8973],[116.4771,40.9],[116.4757,40.9124],[116.4674,40.9316],[116.4619,40.933],[116.4565,40.9447],[116.4482,40.9543],[116.4537,40.9666],[116.4565,40.9783],[116.4633,40.9845],[116.4743,40.9783],[116.4853,40.9824],[116.4935,40.9783],[116.5169,40.9756],[116.5196,40.9818],[116.5416,40.9907],[116.5471,40.9879],[116.5581,40.9886],[116.5594,40.9927],[116.5746,40.9879],[116.5855,40.9797],[116.5993,40.9756],[116.6144,40.9852],[116.6171,40.9927],[116.6158,41.0044],[116.6212,41.0161],[116.6226,41.0278],[116.6144,41.0367],[116.6185,41.049],[116.6158,41.0532],[116.6254,41.0552],[116.6309,41.0607],[116.6377,41.0607],[116.6487,41.0573],[116.6583,41.0504],[116.683,41.0415],[116.6899,41.0442],[116.6982,41.0257],[116.6968,41.0161],[116.6913,41.0126],[116.6913,41.0085],[116.6844,41.0003],[116.683,40.9948],[116.6844,40.9824],[116.6776,40.9763],[116.6776,40.9721],[116.6872,40.9618],[116.6913,40.9502],[116.7023,40.9412],[116.7064,40.9344],[116.7174,40.9357],[116.7242,40.9296],[116.716,40.9186],[116.7133,40.9103],[116.7256,40.9035],[116.7311,40.8973],[116.7366,40.8973],[116.7517,40.8911],[116.7599,40.8904],[116.7586,40.8836],[116.7709,40.8808],[116.7833,40.8733],[116.7929,40.8643],[116.7984,40.854],[116.8025,40.8513],[116.8025,40.843],[116.8053,40.8403],[116.8121,40.8479],[116.8204,40.8485],[116.8231,40.843],[116.8369,40.8417],[116.8561,40.8348],[116.8602,40.8293],[116.8616,40.8252],[116.8753,40.8218],[116.8808,40.8149],[116.8822,40.8032],[116.8863,40.8012],[116.8808,40.7977],[116.8739,40.7991],[116.8712,40.7943],[116.8629,40.7922],[116.8671,40.7847],[116.8506,40.7785],[116.8506,40.7751],[116.8341,40.7703],[116.8396,40.7607],[116.8272,40.749],[116.819,40.7504],[116.8025,40.7462],[116.7902,40.7517],[116.7833,40.7579],[116.7819,40.7524],[116.7902,40.727],[116.7888,40.714],[116.7847,40.7016],[116.7586,40.7064],[116.7476,40.6968],[116.7311,40.6913],[116.7146,40.6796],[116.7146,40.668],[116.7091,40.6618],[116.7119,40.6556],[116.7119,40.6425],[116.7023,40.6316],[116.7064,40.624],[116.7023,40.6151],[116.7078,40.6075],[116.705,40.6027],[116.7105,40.6007],[116.7091,40.5904],[116.7119,40.5849],[116.7146,40.5746],[116.7064,40.5643],[116.6982,40.5656],[116.6844,40.554],[116.6776,40.5519],[116.7091,40.543],[116.7091,40.532],[116.7174,40.5251],[116.7133,40.5231],[116.7119,40.5183],[116.7009,40.508],[116.6982,40.4942],[116.6927,40.4881],[116.7009,40.4812],[116.7078,40.4661],[116.7229,40.4475],[116.7229,40.4427],[116.7201,40.4386],[116.7188,40.4297],[116.7201,40.4235],[116.7242,40.4166],[116.7242,40.4036],[116.7215,40.396],[116.7119,40.3919],[116.7091,40.3871],[116.7119,40.3802],[116.7064,40.3761],[116.7133,40.3686],[116.7188,40.3693],[116.7188,40.361],[116.727,40.361],[116.7229,40.3397],[116.7311,40.3391],[116.7297,40.3349],[116.7448,40.3391],[116.7448,40.3349],[116.7503,40.3315],[116.7517,40.3267],[116.7682,40.3253],[116.7696,40.3219],[116.7668,40.3143],[116.7435,40.2944],[116.7435,40.2889],[116.7352,40.2793],[116.7229,40.2697],[116.705,40.2484],[116.6927,40.2395],[116.6844,40.2292],[116.6762,40.2319],[116.6707,40.2443],[116.6762,40.2477],[116.6762,40.2553],[116.6652,40.2621],[116.6432,40.2573],[116.6267,40.2608],[116.6212,40.2505],[116.6034,40.2512],[116.6006,40.2608],[116.591,40.2656],[116.5883,40.2718],[116.5704,40.2773],[116.554,40.2773],[116.5388,40.2718],[116.5265,40.2621],[116.51,40.2587],[116.4839,40.2738],[116.4798,40.28],[116.4702,40.2807],[116.4647,40.2855],[116.4551,40.2889],[116.4482,40.2999],[116.4441,40.3027],[116.4551,40.313],[116.4468,40.3233],[116.4386,40.3246],[116.4345,40.3288],[116.4166,40.3308],[116.407,40.3356],[116.3947,40.3363],[116.3919,40.3391],[116.3878,40.3487],[116.3809,40.3528],[116.3672,40.3672],[116.3452,40.3734],[116.3287,40.3851],[116.3109,40.3892],[116.2903,40.3837],[116.2875,40.405],[116.2971,40.4132],[116.2917,40.4386],[116.2944,40.4503],[116.3068,40.4661],[116.2999,40.4702],[116.293,40.4784],[116.2917,40.4853],[116.2999,40.4867],[116.3274,40.4997],[116.3631,40.5004],[116.3768,40.4956],[116.3741,40.4908],[116.3782,40.4846],[116.3782,40.4791],[116.4166,40.4832],[116.4331,40.4784],[116.4441,40.4812],[116.4565,40.4812],[116.4578,40.4887],[116.4798,40.4846],[116.4894,40.4812],[116.5059,40.4826],[116.5182,40.4915],[116.5196,40.4963],[116.5031,40.5093],[116.4977,40.5183],[116.4771,40.5141],[116.4743,40.5169],[116.4647,40.5196],[116.4619,40.5251],[116.4812,40.545],[116.4839,40.5526],[116.4963,40.5547],[116.5169,40.5773]]]}},
]};
格式化后,可以看出这个json文件比其他地图json中,多了两个属性。
scale,这个区域的缩放系数。
childNum,下级包含的个数。
绘制地图
var height = 600;
var width = 600;
var texts,centered;
var color = {
lineColor:"#4AFFFE",
pointColor:"#fbcd2c",
lightColor:"#4affff",
areaColor:["#09373a","#11545c","#0b6c79","#769398"],
locationBgColor:"#0e403f",
pointTextColor:"#fff",
haveSelectColor:"#1c2b2b"
};
var scale = 1;
var projection = d3.geo.mercator()
.center([100, 36])
.scale(800)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("#map").append('svg')
.attr('id',"mapSvg")
.attr('width',width)
.attr('height',height);
svg.append("rect")
.attr("class", "background")
.attr("width", width)
.attr("height", height)
.on("click", clicked);
var mapG = svg.append('g')
.attr('id',"mapG");
mapG.selectAll("path")
.data(chinajson.features)
.enter()
.append("path")
.attr("stroke",color.lineColor)
.attr("stroke-width", 1)
.attr("fill","#000")
.attr("id", function(d) {
return "path" + d.properties.id;
})
.attr("style","display:block")
.attr("class", function(d) {
if (d.properties.id.length > 2) {
return "location"
}else{
return "distribution"
}
})
.attr("style","cursor:pointer;")
.attr("d", path)
.on("mouseover", function(d){
d3.select(this).attr("stroke-width",5/self.scale + "px");
d3.select(this).attr("stroke",color.pointColor);
})
.on("mouseout",function(d){
if(d3.select(this).attr("active") == "false" || d.properties.id.length<=2 || !d3.select(this).attr("active")) {
d3.select(this)
.attr("stroke", color.lineColor)
.attr("stroke-width",1/scale + "px");
}
})
.on("click",function(d){
if(d.properties.id.length<=2){
clicked(d)
};
if(d.properties.id.length>2){
d3.select(".point-info").attr("style","display:block;");
mapG.selectAll("path")
.attr("stroke",color.lineColor)
.attr("stroke-width",1/scale + "px")
.attr("active","false");
d3.select(this)
.attr("stroke",self.color.pointColor)
.attr("stroke-width",5/scale + "px")
.attr("active","true");
}
});
texts = mapG.append("g")
.attr("class", "texts")
.selectAll("text")
.data(chinajson.features)
.enter().append("text")
.attr("class", function(d) {
if (d.properties.id.length > 2) {
return "location"
}else{
return "distribution"
}
})
.text(function(d) {
return d.properties.name;
})
.attr("id", function(d) {
return "text" + d.properties.id;
})
.attr("transform", function(d) {
var centroid = path.centroid(d),
x = centroid[0],
y = centroid[1];
return "translate(" + x + ", " + y + ")";
})
.attr('fill', '#1E9AAD')
.attr('font-size', '14px')
.attr("style","cursor:pointer;");
d3.selectAll(".location").attr("style","display:none");
和普通的绘制没太大区别,区别在于利用每一块的properties属性中id的长度分辨其层级,比如北京的id长度是2,海淀区的id长度是6.
实现点击缩放
function clicked(d) {
var x, y, k;
if (d && centered !== d) {
var centroid = path.centroid(d);
x = centroid[0];
y = centroid[1];
if(d.properties.id == "46"){
y = y+10;
}
if(d.properties.scale){
k = d.properties.scale;
}else{
k=scale
}
scale = k;
centered = d;
$(".re-show").css("display","block");
mapChange("open",d);
} else {
x = width / 2;
y = height / 2;
k = 1;
scale = 1;
centered = null;
$(".re-show").css("display","none");
mapChange("close");
}
mapG.selectAll("path")
.classed("active", centered && function(d) { return d === centered; });
mapG.transition()
.duration(750)
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")scale(" + k + ")translate(" + -x + "," + -y + ")")
.style("stroke-width", 1.5 / k + "px");
}
这个缩放看起来复杂其实很简单,就是放大整个地图,并把地图的中心位置改为当前点击省市的中心,这样看起来就会有点哪里哪里放大的效果。因为不同省市的区域面积不相同,所以不同的省市放大的倍数也不相同,这时就要根据json中的scale来确定放大的倍数。
地图缩放变化所带来的其他改变
function mapChange(flag, d) {
if (flag == "open") {
var disId = d.properties.id;
if (disId.length < 3) {
chinajson.features.forEach(function(n) {
if (n.properties.id.length > 2 && n.properties.id.substr(0, 2) === disId) {
d3.select("#path" + n.properties.id)
.attr("style", "display:block")
.attr("stroke-width", 1 / scale + "px")
.attr("fill", "#1b5c5b");
d3.select("#text" + n.properties.id)
.attr("style", "display:block")
.attr("font-size", 0.8 + "px");
} else {
if (n.properties.id.length <= 2 && n.properties.id != disId) {
d3.select(".point-info").attr("style", "display:none;");
d3.select("#path" + n.properties.id)
.attr("style", "opacity:0.2")
.attr("stroke-width", "0.3px");
d3.select("#text" + n.properties.id)
.attr("style", "display:none")
.attr("font-size", "3px");
} else {
d3.select("#path" + n.properties.id)
.attr("style", "display:none")
.attr("stroke-width", "0.2px");
d3.select("#text" + n.properties.id)
.attr("style", "display:none")
.attr("font-size", "1px");
}
}
})
}
} else {
d3.selectAll(".location").attr("style", "display:none");
d3.selectAll("text").attr("font-size", "14px");
d3.selectAll("path").attr("stroke-width", "1px");
d3.selectAll(".distribution").attr("style", "display:block");
}
}
这里随区域缩放变化的属性主要有线的粗细及字体的大小,根据当前缩放倍数确定即可。
完整的地图文件
这个地图文件是我呕心沥血整理及修改的。包括了全国的省市区坐标及各项属性。望大家妥善使用。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。