|  | <!DOCTYPE html> | 
        
          |  |  | 
        
          |  | <head> | 
        
          |  | <meta charset="utf-8"> | 
        
          |  | <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> | 
        
          |  | <script src="https://d3js.org/topojson.v1.min.js"></script> | 
        
          |  | <link rel="stylesheet" href="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.css" /> | 
        
          |  | <script src="http://cdn.leafletjs.com/leaflet/v0.7.7/leaflet.js"></script> | 
        
          |  | <style> | 
        
          |  | #mapid { | 
        
          |  | width: 960px; | 
        
          |  | height: 720px; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | svg { | 
        
          |  | position: relative; | 
        
          |  | } | 
        
          |  |  | 
        
          |  | path { | 
        
          |  | /*fill: #000;*/ | 
        
          |  | fill-opacity: .0; | 
        
          |  | stroke: brown; | 
        
          |  | stroke-width: 1.5px; | 
        
          |  | } | 
        
          |  | /*path:hover { | 
        
          |  | fill: brown; | 
        
          |  | fill-opacity: .7; | 
        
          |  | }*/ | 
        
          |  |  | 
        
          |  | .tooltip { | 
        
          |  | position: absolute; | 
        
          |  | z-index: 10; | 
        
          |  | font-size: 8pt; | 
        
          |  | border: black; | 
        
          |  | border-style: solid; | 
        
          |  | border-width: 1px; | 
        
          |  | } | 
        
          |  | </style> | 
        
          |  | </head> | 
        
          |  |  | 
        
          |  | <body> | 
        
          |  | <div id="mapid"></div> | 
        
          |  | <script> | 
        
          |  | var mymap = L.map('mapid', { | 
        
          |  | center: [22.73, 120.42], | 
        
          |  | zoom: 10 | 
        
          |  | // dragging: false, | 
        
          |  | // scrollWheelZoom: 'center', | 
        
          |  | // doubleClickZoom: 'center', | 
        
          |  | // boxZoom: false | 
        
          |  | }); // center 跟 zoom 這兩個 option 也可以改寫成 .setView([23.6, 121], 8); | 
        
          |  |  | 
        
          |  | // 讀取 OpenStreetMap 的 Map Tile | 
        
          |  | L.tileLayer("http://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { | 
        
          |  | attribution: '<a href="http://openstreetmap.org">OpenStreetMap</a>' | 
        
          |  | }).addTo(mymap); | 
        
          |  |  | 
        
          |  | // 在 .leaflet-objects-pane --> .leaflet-overlay-pane 裡面新增 Element | 
        
          |  | var svg = d3.select(mymap.getPanes().overlayPane).append("svg"), | 
        
          |  | g = svg.append("g").attr("class", "leaflet-zoom-hide"); | 
        
          |  |  | 
        
          |  | // 讀取、綁定資料 | 
        
          |  | d3.json("kh_topojson.json", function(error, kh) { | 
        
          |  | if (error) throw error; | 
        
          |  |  | 
        
          |  | // 把 GeoJSON 轉換成 SVG | 
        
          |  | var transform = d3.geo.transform({ | 
        
          |  | point: projectPoint | 
        
          |  | }), | 
        
          |  | path = d3.geo.path().projection(transform); | 
        
          |  |  | 
        
          |  | // 用 Data Join 的方式加入 Path | 
        
          |  | // 這時候還沒有實際去畫出 Path,等下才要加入 `.attr("d", path);`,這邊先等等 | 
        
          |  | var feature = g.selectAll("path") | 
        
          |  | .data(topojson.feature(kh, kh.objects.kh_bike_geojson_edited).features) | 
        
          |  | .enter().append("path"); | 
        
          |  |  | 
        
          |  | // Smooth Path | 
        
          |  | // Ref: http://bl.ocks.org/shimizu/1d32c3dfc6cec3b337f2 | 
        
          |  | var interpolate = d3.svg.line() | 
        
          |  | .x(function(d) { | 
        
          |  | return d[0] | 
        
          |  | }).y(function(d) { | 
        
          |  | return d[1] | 
        
          |  | }).interpolate("basis"); | 
        
          |  |  | 
        
          |  | var smoothPath = function(pstr) { | 
        
          |  | var sp = path(pstr).replace(/M|Z/, "").split("L").map(function(d) { | 
        
          |  | return d.split(",") | 
        
          |  | }); | 
        
          |  | return interpolate(sp); | 
        
          |  | }; | 
        
          |  |  | 
        
          |  | // 加入 Tooltip | 
        
          |  | var tooltip = d3.select("body") | 
        
          |  | .data(topojson.feature(kh, kh.objects.kh_bike_geojson_edited).features) | 
        
          |  | .append("div") | 
        
          |  | .attr("class", "tooltip") | 
        
          |  | .style("visibility", "hidden"); | 
        
          |  |  | 
        
          |  | // 每次圖片一有什麼動作(移動、縮放、等等等),就要重新繪圖 | 
        
          |  | mymap.on("viewreset", reset); | 
        
          |  | reset(); | 
        
          |  |  | 
        
          |  |  | 
        
          |  | // Reposition the SVG to cover the features. | 
        
          |  | function reset() { | 
        
          |  | // SVG 的尺寸很難界定,因為使用者會對 SVG 放大縮小,所以指定一個固定的尺寸並不切實際 | 
        
          |  | // 要用 D3 的 Path bounds 來幫忙(https://github.com/mbostock/d3/wiki/Geo-Paths#wiki-path_bounds) | 
        
          |  | var bounds = path.bounds(topojson.feature(kh, kh.objects.kh_bike_geojson_edited)), | 
        
          |  | topLeft = bounds[0], | 
        
          |  | bottomRight = bounds[1]; | 
        
          |  |  | 
        
          |  | svg.attr("width", bottomRight[0] - topLeft[0]) | 
        
          |  | .attr("height", bottomRight[1] - topLeft[1]) | 
        
          |  | .style("left", topLeft[0] + "px") | 
        
          |  | .style("top", topLeft[1] + "px"); | 
        
          |  |  | 
        
          |  | g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")"); | 
        
          |  |  | 
        
          |  | feature.attr("d", function(d) { | 
        
          |  | return smoothPath(d); | 
        
          |  | }) | 
        
          |  | .on("mouseover", function() { | 
        
          |  | return tooltip.style("visibility", "visible"); | 
        
          |  | }) | 
        
          |  | .on("mousemove", function(d) { | 
        
          |  | return tooltip.style("top", (d3.event.pageY - 10) + "px").style("left", (d3.event.pageX + 10) + "px").text(d.properties.name); | 
        
          |  | }) | 
        
          |  | .on("mouseout", function() { | 
        
          |  | return tooltip.style("visibility", "hidden"); | 
        
          |  | }); | 
        
          |  | } | 
        
          |  |  | 
        
          |  | // Use Leaflet to implement a D3 geometric transformation. | 
        
          |  | // 因為 D3 用來處理 Projection 的系統跟 Leaflet 不一樣,所以用個函式來轉換 | 
        
          |  | // 官方說法:D3 and Leaflet use different APIs for rendering shapes and projecting points. | 
        
          |  | function projectPoint(x, y) { | 
        
          |  | var point = mymap.latLngToLayerPoint(new L.LatLng(y, x)); | 
        
          |  | this.stream.point(point.x, point.y); | 
        
          |  | } | 
        
          |  |  | 
        
          |  |  | 
        
          |  | }); | 
        
          |  | </script> | 
        
          |  | </body> |