구글맵(차트)

Language/JAVASCRIPT 2014. 12. 12. 16:43

https://developers.google.com/chart/

https://developers.google.com/chart/

https://developers.google.com/maps/documentation/javascript/examples/overlay-hideshow?hl=ko

http://using.tistory.com/82

http://roadmaps.kr/grboard/board.php?id=tips&articleNo=19

http://jsfiddle.net/msoliman85/XyFDM/ 


<!DOCTYPE html>
<html>
 
<head>
   
<meta name="viewport" content="initial-scale=1.0, user-scalable=no">
   
<meta charset="utf-8">
   
<title>Showing/Hiding overlays</title>
   
<style>
      html
, body, #map-canvas {
       
height: 100%;
       
margin: 0px;
       
padding: 0px
     
}
     
#panel {
       
position: absolute;
       
top: 5px;
       
left: 50%;
       
margin-left: -180px;
       
z-index: 5;
       
background-color: #fff;
       
padding: 5px;
       
border: 1px solid #999;
     
}
   
</style>
   
<script src="https://maps.googleapis.com/maps/api/js?v=3.exp"></script>
   
<script>
// This example adds hide() and show() methods to a custom overlay's prototype.
// These methods toggle the visibility of the container <div>.
// Additionally, we add a toggleDOM() method, which attaches or detaches the
// overlay to or from the map.

var overlay;

USGSOverlay.prototype = new google.maps.OverlayView();

function initialize() {
 
var myLatLng = new google.maps.LatLng(62.323907, -150.109291);
 
var mapOptions = {
    zoom
: 11,
    center
: myLatLng,
    mapTypeId
: google.maps.MapTypeId.SATELLITE
 
};

 
var map = new google.maps.Map(document.getElementById('map-canvas'),
      mapOptions
);

 
var swBound = new google.maps.LatLng(62.281819, -150.287132);
 
var neBound = new google.maps.LatLng(62.400471, -150.005608);
 
var bounds = new google.maps.LatLngBounds(swBound, neBound);

 
// The photograph is courtesy of the U.S. Geological Survey.
 
var srcImage = 'https://developers.google.com/maps/documentation/javascript/';
  srcImage
+= 'examples/full/images/talkeetna.png';

  overlay
= new USGSOverlay(bounds, srcImage, map);
}

/** @constructor */
function USGSOverlay(bounds, image, map) {

 
// Now initialize all properties.
 
this.bounds_ = bounds;
 
this.image_ = image;
 
this.map_ = map;

 
// Define a property to hold the image's div. We'll
 
// actually create this div upon receipt of the onAdd()
 
// method so we'll leave it null for now.
 
this.div_ = null;

 
// Explicitly call setMap on this overlay
 
this.setMap(map);
}

/**
 * onAdd is called when the map's panes are ready and the overlay has been
 * added to the map.
 */

USGSOverlay.prototype.onAdd = function() {

 
var div = document.createElement('div');
  div
.style.border = 'none';
  div
.style.borderWidth = '0px';
  div
.style.position = 'absolute';

 
// Create the img element and attach it to the div.
 
var img = document.createElement('img');
  img
.src = this.image_;
  img
.style.width = '100%';
  img
.style.height = '100%';
  div
.appendChild(img);

 
this.div_ = div;

 
// Add the element to the "overlayImage" pane.
 
var panes = this.getPanes();
  panes
.overlayImage.appendChild(this.div_);
};

USGSOverlay.prototype.draw = function() {

 
// We use the south-west and north-east
 
// coordinates of the overlay to peg it to the correct position and size.
 
// To do this, we need to retrieve the projection from the overlay.
 
var overlayProjection = this.getProjection();

 
// Retrieve the south-west and north-east coordinates of this overlay
 
// in LatLngs and convert them to pixel coordinates.
 
// We'll use these coordinates to resize the div.
 
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
 
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());

 
// Resize the image's div to fit the indicated dimensions.
 
var div = this.div_;
  div
.style.left = sw.x + 'px';
  div
.style.top = ne.y + 'px';
  div
.style.width = (ne.x - sw.x) + 'px';
  div
.style.height = (sw.y - ne.y) + 'px';
};

USGSOverlay.prototype.onRemove = function() {
 
this.div_.parentNode.removeChild(this.div_);
};

// [START region_hideshow]
// Set the visibility to 'hidden' or 'visible'.
USGSOverlay.prototype.hide = function() {
 
if (this.div_) {
   
// The visibility property must be a string enclosed in quotes.
   
this.div_.style.visibility = 'hidden';
 
}
};

USGSOverlay.prototype.show = function() {
 
if (this.div_) {
   
this.div_.style.visibility = 'visible';
 
}
};

USGSOverlay.prototype.toggle = function() {
 
if (this.div_) {
   
if (this.div_.style.visibility == 'hidden') {
     
this.show();
   
} else {
     
this.hide();
   
}
 
}
};

// Detach the map from the DOM via toggleDOM().
// Note that if we later reattach the map, it will be visible again,
// because the containing <div> is recreated in the overlay's onAdd() method.
USGSOverlay.prototype.toggleDOM = function() {
 
if (this.getMap()) {
   
// Note: setMap(null) calls OverlayView.onRemove()
   
this.setMap(null);
 
} else {
   
this.setMap(this.map_);
 
}
};
// [END region_hideshow]

google
.maps.event.addDomListener(window, 'load', initialize);

   
</script>
 
</head>
 
<body>

<!-- [START region_toolbar] -->
<!-- Add an input button to initiate the toggle method on the overlay. -->
   
<div id ="panel">
     
<input type="button" value="Toggle visibility" onclick="overlay.toggle();"></input>
     
<input type="button" value="Toggle DOM attachment" onclick="overlay.toggleDOM();"></input>
   
</div>
<!-- [END region_toolbar] -->
   
<div id="map-canvas"></div>
 
</body>
</html>

맞춤형 오버레이


Google 지도 API V3는 맞춤형 오버레이를 만들 수 있도록 OverlayView 클래스를 제공합니다. OverlayView는 오버레이를 만들 때 구현해야 할 여러 메소드를 제공하는 기본 클래스입니다. 클래스는 지도 상의 위치와 화면 좌표 간에 변환을 가능하게 하는 몇 개의 메소드도 제공합니다. 맞춤형 오버레이를 만들려면 다음과 같이 수행합니다.

  • 맞춤형 객체의 prototype을 google.maps.OverlayView()의 새 인스턴스로 설정합니다. 이렇게 하면 오버레이 클래스를 효과적으로 \'상속\'할 수 있습니다.
  • 맞춤형 오버레이의 생성자를 만들고 초기화 매개변수를 해당 생성자 내의 맞춤형 속성으로 설정합니다.
  • 프로토타입 내에서 onAdd() 메소드를 구현하고 지도에 오버레이를 연결합니다. OverlayView.onAdd()는 지도에 오버레이를 연결할 준비가 된 경우에 호출됩니다.
  • 프로토타입 내에서 draw() 메소드를 구현하고 객체의 시각적 표시를 처리합니다. 마찬가지로 OverlayView.draw()는 객체가 처음 표시되는 경우 호출됩니다.
  • 또한 onRemove() 메소드를 구현하여 오버레이 내에서 추가한 요소를 정리해야 합니다.

다음은 세부 단계에 대한 설명입니다.

오버레이 상속하기


OverlayView를 사용하여 간단한 이미지 오버레이를 만듭니다(V2 API 내의 GGroundOverlay와 유사). 이미지 경계 및 해당 지역의 USGS 이미지를 포함하는USGSOverlay 객체를 만듭니다. var overlay;

function initialize(){
  
var myLatLng =new google.maps.LatLng(62.323907,-150.109291);
  
var myOptions ={
    zoom
:11,
    center
: myLatLng,
    mapTypeId
: google.maps.MapTypeId.SATELLITE
  
};

  
var map =new google.maps.Map(document.getElementById(\"map_canvas\"), myOptions);

  
var swBound =new google.maps.LatLng(62.281819,-150.287132);
  
var neBound =new google.maps.LatLng(62.400471,-150.005608);
  
var bounds =new google.maps.LatLngBounds(swBound, neBound);

  
// Photograph courtesy of the U.S. Geological Survey
  
var srcImage =\'images/talkeetna.png\';
  overlay 
=newUSGSOverlay(bounds, srcImage, map);
} 다음으로 해당 클래스의 생성자를 만들고 전달된 매개변수를 새 객체의 속성으로 초기화합니다. OverlayView에서 USGSOverlay도 명시적으로 상속해야 합니다. 새 클래스의 prototype을 부모 클래스의 인스턴스로 설정하여 이 작업을 수행합니다. 여기서는 부모 클래스를 수정하려 하지 않으므로 부모 클래스 그 자체보다는 인스턴스로 프로토타입을 설정합니다.functionUSGSOverlay(bounds, image, map){

  
// Now initialize all properties.
  
this.bounds_ = bounds;
  
this.image_ = image;
  
this.map_ = map;

  
// We define a property to hold the image\'s
  
// div. We\'ll actually create this div
  
// upon receipt of the add() method so we\'ll
  
// leave it null for now.
  
this.div_ =null;

  
// Explicitly call setMap() on this overlay
  
this.setMap(map);
}

USGSOverlay.prototype =new google.maps.OverlayView();아직까지는 이 오버레이를 오버레이 생성자의 지도에 연결할 수 없습니다. 특히, 모든 지도 페인(객체가 지도에 표시되는 순서 지정)이 사용 가능한지 확인해야 합니다. 편리하게도, API는 이와 같은 경우가 발생했음을 알려주는 도우미 메소드를 제공합니다. 이 메소드는 다음 섹션에서 처리합니다.

오버레이 초기화하기


오버레이가 최초로 인스턴스화되어 표시할 준비가 완료되면 브라우저의 DOM을 사용하여 지도에 연결해야 합니다. API는 오버레이의 onAdd() 메소드를 호출하여 오버레이가 지도에 추가되었음을 표시합니다. 이미지를 포함할 를 만들어 이 메소드를 처리하고 \"\" 요소를 추가하고 이 요소를 에 연결한 다음, 마지막으로 지도의 페인(pane) 중 하나에 오버레이를 연결합니다. 페인(pane)은 DOM 트리에 있는 노드를 말합니다. MapPanes 유형의 페인(pane) 집합은 지도에 있는 여러 레이어의 스택 순서(stacking order)를 지정합니다. 다음과 같은 페인(pane)이 가능하며 아래쪽에서 위쪽으로 쌓여지는 순서로 열거됩니다.

  • MapPanes.mapPane
  • MapPanes.overlayLayer
  • MapPanes.overlayShadow
  • MapPanes.overlayImage
  • MapPanes.floatShadow
  • MapPanes.overlayMouseTarget
  • MapPanes.floatPane

Google은 \'지상 오버레이\' 이미지를 제공하므로 overlayLayer 지도 페인(pane)을 사용합니다. 지도 페인(pane)을 가지고 있으면 객체가 지도 페인에 자식으로 연결됩니다. USGSOverlay.prototype.onAdd =function(){

  
// Note: an overlay\'s receipt of onAdd() indicates that
  
// the map\'s panes are now available for attaching
  
// the overlay to the map via the DOM.

  
// Create the DIV and set some basic attributes.
  
var div = document.createElement(\'DIV\');
  div
.style.border =\"none\";
  div
.style.borderWidth =\"0px\";
  div
.style.position =\"absolute\";

  
// Create an IMG element and attach it to the DIV.
  
var img = document.createElement(\"img\");
  img
.src =this.image_;
  img
.style.width =\"100%\";
  img
.style.height =\"100%\";
  div
.appendChild(img);

  
// Set the overlay\'s div_ property to this DIV
  
this.div_ = div;

  
// We add an overlay to a map via one of the map\'s panes.
  
// We\'ll add this overlay to the overlayImage pane.
  
var panes =this.getPanes();
  panes
.overlayLayer.appendChild(div);
}

오버레이 그리기


위에서 특수한 시각적 표시를 실제로 호출한 것은 아닙니다. API는 지도에 오버레이를 그릴 때마다(처음 추가한 경우 포함) 오버레이에 별도의 draw() 메소드를 호출합니다. 따라서 이 draw() 메소드를 구현하고 getProjection()을 사용하여 오버레이의 MapCanvasProjection를 검색하고 객체의 오른쪽 상단과 왼쪽 하단 지점을 배치할 정확한 좌표를 계산합니다. 여기에서  크기가 조정됩니다. 이렇게 하면 오버레이 생성자에서 지정한 경계와 일치하도록 이미지 크기가 조정됩니다.USGSOverlay.prototype.draw =function(){

  
// Size and position the overlay. We use a southwest and northeast
  
// position of the overlay to peg it to the correct position and size.
  
// We need to retrieve the projection from this overlay to do this.
  
var overlayProjection =this.getProjection();

  
// Retrieve the southwest and northeast coordinates of this overlay
  
// in latlngs and convert them to pixels coordinates.
  
// We\'ll use these coordinates to resize the DIV.
  
var sw = overlayProjection.fromLatLngToDivPixel(this.bounds_.getSouthWest());
  
var ne = overlayProjection.fromLatLngToDivPixel(this.bounds_.getNorthEast());

  
// Resize the image\'s DIV to fit the indicated dimensions.
  
var div =this.div_;
  div
.style.left = sw.x +\'px\';
  div
.style.top = ne.y +\'px\';
  div
.style.width =(ne.x - sw.x)+\'px\';
  div
.style.height =(sw.y - ne.y)+\'px\';
}

오버레이 삭제하기


onRemove() 메소드를 추가하여 지도에서 오버레이를 완전히 삭제할 수도 있습니다. 이 메소드는 오버레이의 map 속성을 null로 설정한 적이 있으면 API에서 자동으로 호출됩니다. USGSOverlay.prototype.onRemove =function(){
  
this.div_.parentNode.removeChild(this.div_);
  
this.div_ =null;
} 

예제 보기(overlay-simple.html) 

오버레이 숨기기 및 표시하기


오버레이를 단순히 만들거나 삭제하는 것이 아니라 숨기거나 표시하려면, 자신의 hide() 및 show() 메소드를 구현하여 오버레이의 가시성을 조정할 수 있습니다. 또는 지도의 DOM에서 오버레이를 분리할 수도 있습니다. 그러나 이 작업에는 비용이 약간 더 듭니다. 그리고 나서 오버레이를 지도의 DOM에 다시 연결하면 오버레이의 onAdd() 메소드가 다시 호출됩니다. 다음 예에서는 hide() 및 show() 메소드를 오버레이의 프로토타입에 추가합니다. 이 프로토타입은 컨테이너 의 가시성을 전환합니다. 또한 오버레이를 지도에 연결하거나 분리하는 toogleDOM() 메소드를 추가합니다. 가시성을 \"hidden\"으로 설정한 다음 toggleDOM()을 통해 DOM에서 지도를 분리할 경우, 지도를 나중에 다시 연결하면 지도는 포함하는 가 오버레이의 onAdd() 메소드에서 다시 만들어지기 때문에 다시 표시됩니다. // Note that the visibility property must be a string enclosed in quotes
USGSOverlay.prototype.hide =function(){
  
if(this.div_){
    
this.div_.style.visibility =\"hidden\";
  
}
}

USGSOverlay.prototype.show =function(){
  
if(this.div_){
    
this.div_.style.visibility =\"visible\";
  
}
}

USGSOverlay.prototype.toggle =function(){
  
if(this.div_){
    
if(this.div_.style.visibility ==\"hidden\"){
      
this.show();
    
}else{
      
this.hide();
    
}
  
}
}

USGSOverlay.prototype.toggleDOM =function(){
  
if(this.getMap()){
    
this.setMap(null);
  
}else{
    
this.setMap(this.map_);
  
}
}

// Now we add an input button to initiate the toggle method 
// on the specific overlay
<div id =\"toolbar\" width=\"100%; height:20px;\" style=\"text-align:center\">
  
<input type=\"button\" value=\"Toggle Visibility\" onclick=\"overlay.toggle();\">input>
  <input type=\"button\" value=\"Toggle DOM Attachment\" onclick=\"overlay.toggleDOM();\">input>
</div>




Google Maps + D3.js

d3.js 2014/02/11 11:08

Google Map위에 D3.js를 이용하여 마커 혹은 차트를 랜더링 하는 방법을 알아보자.


우선 Google Map 객체를 생성해보자.

Google Map 객체는 MapOption 줌레벨, 지도유형 등을 세팅 가능하다.

자세한 사항은 여기를 참고하기 바란다.

1
2
3
4
5
var map = new google.maps.Map(d3.select("#map").node(), {
    zoom: 12,
    center: new google.maps.LatLng(37.556059, 126.91009),
    mapTypeId: google.maps.MapTypeId.ROADMAP
});

여기서는 줌레벨과 초기 지도 센터 위도/경도 정보, 그리고 지도유형을 설정하였다.


이제 지도위에 마커정보(위도/경도)와 오버레이 객체를 생성한다.

지도위에 마커나 차트를 랜더링하기 위해서는 오버레이 객체를 생성해야 한다.

오버레이는 지도 상의 위도/경도 좌표에 연결된 객체를 말한다.

지도를 드래그하거나 확대/축소하면 연결된 오버레이도 함께 움직이며 지도에 '추가'하는 객체를 말한다.

1
2
var overlay = new google.maps.OverlayView();
var data = {"우리집":[126.8998768,37.4639925, 15,],"구로디지털단지역":[126.901472,37.48525, 15,2], /*생략*/};

data는 마커용 라벨정보와 위도/경도 정보 마커크기, 그리고 지하철 노선정보가 있다. 이 데이터를 이용하여 D3.js로 Circle를 랜더링할 수 있으며 이 글에서는 해당 정보에서 위도/경도만 사용하여 Pie Chart를 그려보고자 한다.

이제 오버레이에 Pie Chart를 랜더링 해보자.

1
2
3
4
5
overlay.onAdd = function() {
    var layer = d3.select(this.getPanes().overlayMouseTarget).append("div").attr("class", "stations");
         
    overlay.draw = function() {
        var projection = this.getProjection(), padding = 50;

여기까지 코드를 살펴보면 지도에 오버레이 랜더링할 준비가 되었을 경우(onAdd) stations div를 추가하고

오버레이에 초기 랜더링(draw)해준다. getProjection()은 Map상에 표시할 좌표를 계산하기 위한 메소드이다.

여기서 중요한건 overlayMouseTarget이다. 마커나 차트에 이벤트 바인딩을 하려면 해당 속성을 지정해줘야 한다.


이제 실제 마커할 D3.js소스를 추가해보자. 여기서 Pie Chart는 NVD3.js 라이브러리를 사용하였다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
        var marker = layer.selectAll("svg")
            .data(d3.entries(data))
            .each(transform)
            .enter().append("svg:svg")
            .each(transform)
            .attr("class", "marker");
 
        var pieData = [{"key": "One","value" : 29.765957771107} , {"key": "Two","value" : 10} ,{"key": "Three","value" : 32.807804682612} , {"key": "Four","value" : 196.45946739256}];
 
        nv.addGraph(function() {
            var chart = nv.models.pieChart()
                .x(function(d) { return d.key; })
                .y(function(d) { return d.value; })
                .margin({top: 0, right: 0, bottom: 0, left: 0})
                .showLabels(true)
                .labelThreshold(.05)
                .donut(true)
                .showLegend(false);
                           
            marker.datum(pieData).transition().duration(1200).call(chart);
 
            return chart;
        });
        function transform(d) {
            d = new google.maps.LatLng(d.value[1], d.value[0]);
            d = projection.fromLatLngToDivPixel(d);
            return d3.select(this)
                .style("left", (d.x-padding) + "px")
                .style("top", (d.y-padding) + "px");
        }
    }; //overlay.draw end
}; //overlay.onAdd end
overlay.setMap(map);

2번 라인 d3.entries(data)으로 data를 key, value로 변경해준다.

1
[{"key":"우리집","value":[126.8998768,37.4639925,15]},{"key":"구로디지털단지역","value":[126.901472,37.48525,15,2]}];

3번 라인에서 data 갯수만큼 each 메소드이용하여 24-30라인의 transform함수를 호출한다.

transform함수의 역활은 Map상에서 실제 마커될 Pie Chart의 픽셀 위치를 위도/경도를 이용하여 세팅해준다.

8번 라인은 Pie Chart의 Data이다. 10-23라인은 NVD3.js를 이용한 Pie Chart를 추가해준다.


Google Map은 zoom_changed, dragend와 같은 각종 이벤트를 제공한다.

해당 이벤트를 사용하는 방법도 간단하게 살펴보자.

1
2
3
4
5
6
7
8
9
google.maps.event.addListener(map, 'zoom_changed', function() {
    var zoomLevel = map.getZoom();
    var lat_south = map.getBounds().getSouthWest().lat();
    var lat_north = map.getBounds().getNorthEast().lat();
    var lng_west = map.getBounds().getSouthWest().lng();
    var lng_east = map.getBounds().getNorthEast().lng();
    var lat_center = map.getCenter().lat();
    var lng_center = map.getCenter().lng();
});

위 소스를 보면 Google Map에서 zoom_charged 이벤트가 발생할때 Map상에 줌레벨, 위도/경도 정보를 구할 수 있다.

해당 정보를 이용하여 현재 Map상에 표현할 마커나 차트정보를 서버 측에서 구해와 랜더링 할 수 있을 것이다.


참고자료








google.load( 'visualization', '1', { packages:['corechart'] });

function ChartMarker( options ) {
    this.setValues( options );
    
    this.$inner = $('<div>').css({
        position: 'relative',
        left: '-50%', top: '-50%',
        width: options.width,
        height: options.height,
        fontSize: '1px',
        lineHeight: '1px',
        backgroundColor: 'transparent',
        cursor: 'default'
    });

    this.$div = $('<div>')
        .append( this.$inner )
        .css({
            position: 'absolute',
            display: 'none'
        });
};

ChartMarker.prototype = new google.maps.OverlayView;

ChartMarker.prototype.onAdd = function() {
    $( this.getPanes().overlayMouseTarget ).append( this.$div );
};

ChartMarker.prototype.onRemove = function() {
    this.$div.remove();
};

ChartMarker.prototype.draw = function() {
    var marker = this;
    var projection = this.getProjection();
    var position = projection.fromLatLngToDivPixel( this.get('position') );

    this.$div.css({
        left: position.x,
        top: position.y,
        display: 'block'
    })

    this.$inner
        .html( '<img src="' + this.get('image') + '"/>' )
        .click( function( event ) {
            var events = marker.get('events');
            events && events.click( event );
        });
        
    this.chart = new google.visualization.PieChart( this.$inner[0] );
    this.chart.draw( this.get('chartData'), this.get('chartOptions') );
};

function initialize() {
    var latLng = new google.maps.LatLng( 40.708762, -74.006731 );

    var map = new google.maps.Map( $('#map_canvas')[0], {
        zoom: 15,
        center: latLng,
        mapTypeId: google.maps.MapTypeId.ROADMAP
    });
    
    var data = google.visualization.arrayToDataTable([
        [ 'Task', 'Hours per Day' ],
        [ 'Work', 1 ],
        [ 'Eat', 9 ]
    ]);
    var color = ['red','blue' ];
    
                 
    var rotation = [ 100, 140];
    for(i=0; i<2; i++){
                    
    var options = {
        fontSize: 8,
        backgroundColor: 'transparent',
        legend: 'none',
         pieStartAngle: rotation[i],
        pieSliceBorderColor: 'transparent',
       slices: {
            0: { color: color[i] },
            1: { color: 'transparent' }
          }        
    };
    var marker = new ChartMarker({
        map: map,
        position: latLng,
        width: '80px',
        height: '80px',
        chartData: data,
        chartOptions: options,
       
        events: {
            click: function( event ) {
                alert( 'Clicked marker' );
            }
        }
    });
}
};

$( initialize );



<script type="text/javascript" src="https://www.google.com/jsapi"></script><script type="text/javascript" src="http://maps.google.com/maps/api/js?sensor=false"></script>


<div id="map_canvas" style="height: 400px; width: 400px"></div>


'Language > JAVASCRIPT' 카테고리의 다른 글

pply(), call() 메서드  (0) 2016.12.15
arguments 객체  (0) 2016.12.15
prototypejs  (0) 2013.11.13
마우스 커서 포지션 이동  (0) 2013.05.22
XMLHttpRequest 고급 기능  (1) 2013.04.29
: