(왜.. 자동 배포가 꺼져있는지 아리송... 매일 새벽에 자동배포 되게끔 되어 있는데.. 음... )



위/경도를 미리 설정하여 마커를 표시하는건 간단하다.

국가(또는 지역)의 정중앙에 마커를 표시하게 되면 따로 마커를 설정할 필요가 없어서 시도해보았다.

결과를 보면 절반의 성공이다....

위도 계산이 안맞다;; 경도는 어찌해서 맞추었는데 위도는 못맞추겠다..

그리고.. 이게 부질없다고 느끼게 한 나라는.. 프랑스(FR)이다

프랑스는 남아메리카에도 영토가 있다;; 
프랑스 정중앙에 마커를 표시하면 대서양에 마커가 찍힌다.. 

고로.. 그냥 마커로 표시하는게 좋을듯하다는게 결론...

국가코드에 KR, US, FR, CA, CN, RU, JP, AU 등등... 

Result


Style
 
body{
	font-size: medium;
}
.jvm-legend {
	line-height: 2em;
	margin-top: 5px;
}
.jvm-legend span {
	vertical-align: middle;
}
.jvm-legend-item {
	width: 2em;
	height: 2em;
	display: inline-block;
	border-style : solid;
	border-color : black;
	border-width : 1px; 
}

.jvm-legend-item-15 {
	background: red;
}
.jvm-legend-item-10 {
	background: orange;
}
.jvm-legend-item-5 {
	background: yellow;
}
.jvm-legend-item-0 {
	background: white;
}

table .td-legend{
	padding-left: 10px
} 

Source
 
var jMap = null;
function numberFormatComma(n) {
	var reg = /(^[+-]?\d+)(\d{3})/;   // 정규식
	n += '';                          // 숫자를 문자열로 변환
	while (reg.test(n)){
		n = n.replace(reg, '$1' + ',' + '$2');
	}
	return n;
}

var countryData = []; 
//for each country, set the code and value
$.each(data.countries, function() {
    countryData[this.code] = this.pGdp;
});

var markers = [ ];

$(document).ready(function(){
	// map
	jMap = $('#jvm_worldMap').vectorMap({
		map : 'world_mill_en',
		series : {
			regions : [ {
				values : countryData,
				scale : [ '#C8EEFF', '#0071A4' ],	// two colors: for minimum and maximum values 
				normalizeFunction : 'polynomial'
			} ]
			,
			markers: [{
	            attribute: 'fill',
	            scale: ['#1B77E0', '#E01B1B'],
	            min: 0,
	            max: 1
	        }]
		},
		//-----------------------------------------------
        // changed to onRegionLabelShow from onLabelShow
        //-----------------------------------------------
        onRegionLabelShow: function(e, el, code) {
            //search through data to find the selected country by it's code
            var country = $.grep(data.countries, function(obj, index) {
                return obj.code == code;
            })[0]; //snag the first one
            //only if selected country was found in dataC
            if (country != undefined) { 
                el.html(el.html() + 
                        "
Code: " +country.code + "
Name: " + country.name + "
GDP/Person: $ " + numberFormatComma(country.pGdp)+ "
Population: "+ numberFormatComma(Math.round(Number(country.pop)/10000))+" 만명"); } } ,markerStyle : { initial : { fill : '#F8E23B', stroke : '#383f47' } }, markers : markers }); $('#countryCode').bind({ keyup : function(e){ switch(e.which){ case 13: var code = e.target.value; var mapObj = jMap.vectorMap('get', 'mapObject'); if(mapObj.regions[code] != undefined){ var bbox = mapObj.regions[code].element.getBBox(); var scale = mapObj.scale; // var centroid = [ (bbox.x * scale) + (bbox.width * scale)/2 + mapObj.transX, (bbox.y * scale) + (bbox.height * scale)/2 + mapObj.transY ]; // var centroid = [ ((bbox.x + bbox.width) * scale)/2 + mapObj.transX, ((bbox.y + bbox.height) * scale)/2 + mapObj.transY ]; // var latLng = mapObj.pointToLatLng( (bbox.x * scale) + (bbox.width * scale)/2 + mapObj.transX, (bbox.y * scale) + (bbox.height * scale)/2 + mapObj.transY ); var zoomMax = (mapObj.params.zoomMax * mapObj.baseScale); var widthPerZoom = bbox.width / zoomMax; var heightPerZoom = bbox.height / zoomMax; var scaleWidth = (scale * widthPerZoom); var scaleHeight = (scale * heightPerZoom); var test = [bbox.x + bbox.width/2, bbox.y + bbox.height/2]; // console.log(test); var latLng = mapObj.pointToLatLng(test[0] * scale, test[1] * scale) // console.log(latLng); var mk = { latLng : [latLng.lat, latLng.lng], name : code, style: {r: 5} } mapObj.addMarkers([mk], []); mapObj.onResize(); }else{ alert('invalid code!!'); } break; } } }) });


HTML
	
국가코드 :


Shape 파일을 열어서 해안선이 실측지도 처럼 나타나는 상황에서 jVectorMap으로 변환할 경우
IE8 이하에서 스크립트 엔진이 멈출 수 있다.


아래는 실측 해안선 일 경우 QGIS에서 확인한 폴리곤 포인트이다.  


위 처럼 포인트가 많을 경우 SVG로 변환할 Path도 많아지므로 VML로 그리다가 멈춘다.

이를 위해 포인트를 정리해주어야 하는데.. 

주위 GIS 개발자에게 물어본 결과 ArcGIS를 이용하여 단순화 할 수 있다고 한다.
그러나 너무 어렵다! 어디 들어가야하는지도 모르겠거니와... 방법도 모르겠고...


구글에서 찾다보니 아래 사이트가 있다.

mapshaper

 저 사이트가 날 살렸다.

웹상에서 슬라이더 조작만으로 포인트를 줄일 수 있다. 

아래처럼....



위 포인트는 눈으로 셀 수 있을만큼 줄어들었다.

실측 지도를 jVectorMap으로 변환하였을때는 17M
간략하게 만든 후 변환하였을때는 396 K

몇배 차이냐....   


한반도 각 시/군/구 또는 광역시도의 데이터는 지역(Region)에 표시하고 특별한 지점(위/경도)도 표시하는 샘플..

티스토리 본문 창이 작은걸 감안하지 않고 페이지를 만들어서 범례가 짤리므로 새창으로 보는걸 추천...

시간이 없어서 복합 데이터 표출을 못한건 안자랑...
그러나 기존 유료 컴퍼넌트를 대체한건 자랑 


Result


Style

body{
	font-size: medium;
}
.jvm-legend {
	line-height: 2em;
	margin-top: 5px;
}
.jvm-legend span {
	vertical-align: middle;
}
.jvm-legend-item {
	width: 2em;
	height: 2em;
	display: inline-block;
	border-style : solid;
	border-color : black;
	border-width : 1px; 
}

.jvm-legend-item-15 {
	background: red;
}
.jvm-legend-item-10 {
	background: orange;
}
.jvm-legend-item-5 {
	background: yellow;
}
.jvm-legend-item-0 {
	background: white;
}

table .td-legend{
	padding-left: 10px
} 

Source

	function numberFormatComma(n) {
		var reg = /(^[+-]?\d+)(\d{3})/;   // 정규식
		n += '';                          // 숫자를 문자열로 변환
		while (reg.test(n)){
			n = n.replace(reg, '$1' + ',' + '$2');
		}
		return n;
	}

	var jMap = null;
	function convert(latitude, longitude) {
		// lat : 위도, long : 경도
		
		// QGIS > SVG 플러그인 속성에 적은 가로-세로
		var width = 823;
		var height = 823;

		// QGIS > SVG 플러그인 속성에서 SET 된 각 한계선
		var eastLong = 132.054062;		// 우측 한계선
		var westLong = 124.410858;		// 좌측 한계선
		var northLat = 39.686848;		// 상측 한계선
		var southLat = 32.043644;		// 하측 한계선
		
		// 경도 -> x
		var longDiff = eastLong - westLong;
		var lon = (longitude - westLong) * (width / longDiff);
		
		// 위도 -> y
		var latDiff = northLat - southLat;
		var lat = (northLat - latitude) * (height / latDiff);
		
		return [ lon, lat];
	}
	
	var sourceData = [
	                  {stnId : '105',	stnNm : '강릉',		lati : '37.75147',	lngt : '128.89098', heatWave : 10},
	                  {stnId : '295',	stnNm : '남해',		lati : '34.81663',	lngt : '127.92641', heatWave : 11},
	                  {stnId : '104',	stnNm : '북강릉',		lati : '37.80456',	lngt : '128.85535', heatWave : 11},
	                  {stnId : '255',	stnNm : '북창원',		lati : '35.2264',	lngt : '128.6725',  heatWave : 16},
	                  {stnId : '121',	stnNm : '영월',		lati : '37.18126',	lngt : '128.45743', heatWave : 18},
	                  {stnId : '211',	stnNm : '인제',		lati : '38.05987',	lngt : '128.16713', heatWave : 7},
	                  {stnId : '192',	stnNm : '진주',		lati : '35.16379',	lngt : '128.04002', heatWave : 14},
	                  {stnId : '155',	stnNm : '창원',		lati : '35.1702',	lngt : '128.57285', heatWave : 16},
	                  {stnId : '95',	stnNm : '철원',		lati : '38.14788',	lngt : '127.3042',   heatWave : 8},
	                  {stnId : '216',	stnNm : '태백',		lati : '37.17031',	lngt : '128.98928', heatWave : 1},
	                  {stnId : '162',	stnNm : '통영',		lati : '34.84546',	lngt : '128.4356',   heatWave : 3}
                  ];
	var markers = [];
	jQuery.each(sourceData, function(){
		var obj = {};
		var color = '';
		obj.coords = convert(this.lati, this.lngt);
		obj.stnNm = this.stnNm;
		obj.stnId = this.stnId;
		obj.heatWave = this.heatWave;
		obj.style = {};
		var val = Number(obj.heatWave);
		if(val >= 15){
			color = 'red';
		}
		else if(val >= 10){
			color = 'orange';
		}
		else if(val >= 5){
			color = 'yellow';
		}
		else {
			color = 'white';
		}
		
		obj.style.fill = color;
		obj.style.r = 5;
		markers.push(obj);
	});
	
	var countryData = []; 
	// 진주시
	countryData['38030'] = 339445;
	countryData['38050'] = 144017;
	countryData['38060'] = 116688;
	countryData['38070'] = 523177;
	countryData['38080'] = 110516;
	countryData['38090'] = 241711;
	countryData['38100'] = 270933;
	countryData['38310'] = 30728;
	countryData['38320'] = 70100;
	countryData['38330'] = 63766;
	countryData['38340'] = 58902;
	
	countryData['11010'] = 143089;
	countryData['11020'] = 143089;
	countryData['11030'] = 256614;
	countryData['11040'] = 307801;
	countryData['11050'] = 385874;
	countryData['11060'] = 377467;
	countryData['11070'] = 425101;
	countryData['11080'] = 492429;
	countryData['11090'] = 347817;
	countryData['11100'] = 365404;
	countryData['11110'] = 605521;
	countryData['11120'] = 506691;
	countryData['11130'] = 321183;
	countryData['11140'] = 395754;
	countryData['11150'] = 501855;
	countryData['11160'] = 574777;
	countryData['11170'] = 456694;
	countryData['11180'] = 262228;
	countryData['11190'] = 430516;
	countryData['11200'] = 414415;
	countryData['11210'] = 543477;
	countryData['11220'] = 441645;
	countryData['11230'] = 568685;
	countryData['11240'] = 686472;
	countryData['11250'] = 494186;

	$(document).ready(function() {
		width = $('#jvm_worldMap').width();

		jMap = $('#jvm_worldMap').vectorMap({
			map : 'korea_mill_en',
			markerStyle : {
				initial : {
					fill : '#F8E23B',
					stroke : '#383f47'
				}
			},
			series : {
				regions : [ {
					values : countryData,
					scale : [ '#C8EEFF', '#0071A4' ],	// two colors: for minimum and maximum values 
					normalizeFunction : 'polynomial'
				} ]
			},
			markers : markers,
			markerStyle : {
				hover: {
					stroke: 'black',
					r : 10
				},
			},
            onMarkerLabelShow: function(e, el, idx) {
           		var msg = el.html();
            	var source = markers[idx];
            	var mapObject = jMap.vectorMap('get', 'mapObject');
            	msg += ""+source.stnNm+"";
            	msg += "
지점ID : "+source.stnId+""; msg += "
폭염일수 : "+source.heatWave+""; // 맵 다시 그리기.. mapObject.onResize(); el.html(msg); }, //----------------------------------------------- // changed to onRegionLabelShow from onLabelShow //----------------------------------------------- onRegionLabelShow: function(e, el, code) { jQuery.each(countryData, function(name, value){ if(name == code && value != undefined){ el.html(el.html() + "
인구 : "+ numberFormatComma(Number(value))); return; } }); } }); var map = $('#jvm_worldMap').vectorMap('get', 'mapObject'); var range = [50000, 100000, 300000, 500000]; jQuery(range).each(function(idx, value){ color = map.series.regions[0].scale.getValue(value); var span = jQuery('').addClass('jvm-legend-item').css('background', color); var valSpan = jQuery('').append(' '+numberFormatComma(value)); jQuery('#regionDiv').append(span).append(valSpan).append('
'); }); });


HTML
	
지점 범례
> 15
> 10
> 5
> 0

지역 범례

  1. 츤데레 2013.11.25 12:18 신고

    안녕하세요. JVectorMap을 이용하고 있는데요..

    마커를 찍고있는데요...

    스위스라는 글이 적히면 스위스에 마커를 표시해야하는데

    lat과 lng를...알아내는 함수가 혹시 존재하나요..?

    • 안녕하세요.
      어떤 맵을 사용하시는지요..?
      기본적인 world-map 사용하시면 lati, lngt로 지점을 표시하실 수 있어요.
      그리고 마우스 동작이 아닌 이벤트로 마커를 표시하는건 구현되어 있지 않을겁니다.

      귀찮아서 저도 못한다고 그러고 넘어갔네요.. ;;

  2. 츤데레 2013.11.25 16:10 신고

    안녕하세요.

    빠른 답변 감사합니다.;

    jquery-jvectormap-1.2.2.min.js 요것을 사용중이구요..

    좀 더 자세히 설명드리면 맵이 로드될 때 해당 나라에 대한 글이 있으면 해당 나라에 마커를 찍어줘야하는데

    도저희 좌표를 알 방법이 없어서.. 이거 일일이 구현해줘야하나요? 따로 API가 존재하는지가 궁금했었습니다..

    이벤트라기 보다는 맵이 로딩될 때 마커를 그려줘야해요..

    현재 스위스에 글을 적으면 객체가 생성되며 이 객체는 국가 코드를 가지고 있고요... JVectorMap에도 regions안에 국가 코드가 있잖아요? 여기서 이 두 코드를 if문 처리하여 true가 반환될 때, 해당 국가에 마커를 그리고 싶은데..ㅠㅠ 좌표가없어서 그리질못하고 있습니당..

    좌표를 첨에 강제적으로 박아주는 방법뿐이 없나요? 그것이 싫어서 이렇게 고생하고 있습니다...

    • 방법이 없으면 그냥 그리면 됩니다...;;

      오늘 작업해볼게요...

      세계지도를 기준으로 국가의 중앙에 마커를 표시하는 샘플을 만들어 올릴테니 참고하시면 될거 같네요..

  3. 츤데레 2013.11.25 18:26 신고

    헉 감사합니다... 현재 point까지 구했는데 map.pointToLatLng 이놈이 자꾸 없는 함수라고 나오네요 크흑..감사합니다. ㅠ

  4. 츤데레 2013.11.26 09:40 신고

    저는 포인트는 구했어요

    https://github.com/bjornd/jvectormap/issues/146 에 있습니다. map의 이름이 달랐어요 ㅠㅠ 저는 mapObject로 설정했는데 ㅠㅠ뻘짓...

  5. 츤데레 2013.11.26 11:52 신고

    좌표가 올바른 좌표가 아니네요...그래도 컨버팅하는 방법은 저것이 맞습니다..

    • 위 계산식으로 하면 좌표가 안찍혀요 ~

      지금 계산한거가... 경도는 맞는데 위도가 안맞네요..
      일단 오늘 저녁에 웹에 올릴게요 -


jVectorMap은 친절히 맵의 Projection을 지원한다. 

국제 표준에 해당하는 Shape 파일을 jVectorMap Converter를 이용할 경우 map projection이 동일하게 변환되지만
한국 지도 행정동 2단계 지도를 변환해 보니 지도 군데 군데 구멍이 뚫리게 되어 이를 사용할 수 없게 되었다. 

관련 글 : http://www.yeory.com/270

 
이 때문에 완벽한 지도를 보여주어야 하기 때문에 QGIS를 이용하여 SVG로 변환하여 지도를 표시하였다.
이 경우 지도는 모두 나오는데 지도 타입(Projection)이 위/경도를 사용할 수 없기 때문에 위/경도로 좌표를 표시할 수 없다.

필요한 것은.... 
  • 지도의 가로 세로 -> 지도 자체의 가로, 세로 사이즈
    (div 등 jVectorMap을 이용하여 그려주는 태그의 사이즈가 아니다.)

  • 지도의 상하좌우 한계점의 위/경도 총 4개
    이 한계점 정보가 정확하지 않으면 X, Y 변환 값이 모두 틀어진다.

이 모든 정보들은 SHP -> SVG로 추출할 때 사용하는 QGIS SVG 플러그인에서 설정이 가능한 정보들이다.

작업 순서는 아래 그림처럼...

< 지도의 가로-세로 사이즈 설정 >

< 가로-세로 설정에 의해 틀어진 지도를 중앙에 위치하도록 설정 >

< 중앙에 맞춰진 지도의 각 상하좌우 위/경도를 설정해 주어야 한다>

마지막 이미지에서 보여지는 정보들이 스크립트에 설정해주어야 하는 정보이다.... 

maxY -> northLat
minY -> southLat
maxX -> eastLong
minY -> westLong

필요한 것이 나왔다면 아래 함수를 이용해 X, Y 변환이 가능하다.
	function convert(latitude, longitude) {
		// lat : 위도, long : 경도
		
		// QGIS > SVG 플러그인 속성에 적은 가로-세로
		var width = 792;
		var height = 611;

		// QGIS > SVG 플러그인 속성에서 SET 된 각 한계선
		var eastLong = 131.754044;		// 우측 한계선
		var westLong = 124.268181;		// 좌측 한계선
		var northLat = 38.749657;		// 상측 한계선
		var southLat = 32.974579;		// 하측 한계선
		
		// 경도 -> x
		var longDiff = eastLong - westLong;
		var lon = (longitude - westLong) * (width / longDiff);
		
		// 위도 -> y
		var latDiff = northLat - southLat;
		var lat = (northLat - latitude) * (height / latDiff);
		
		return [ lon, lat];
	}
경도 1도에 해당하는 픽셀을 구하고... (지도 넓이 / (좌-우)) 표시하고자 하는 경도를 좌측 값으로부터 뺀 후 곱하면 된다. 


모서리에 있는 점은 지도 가로-세로를 표시한 것이고, 위/경도는 이 안쪽을 기준으로 변환이 되어야만 한다.



하루 삽질했으나 원하는 결과가 나왔으니 만족...


jVectorMap은 자체적으로 convert를 사용하여 shp 파일의 vector 이미지를 svg로 변환하여 맵 파일을 생성한다.

그러나 이 컨버터를 이용할 경우 svg 변환 상에 오류가 있는것인지 대한민국 지도 상에 구멍이 슝슝 뚫린다.

아래처럼... 


대구와 대전 일부가 없다.

이를 해결하기 위해 제작자에게 메일도 보냈지만 묵묵부답이라 자체적으로 해결 할 수 밖에... 

1. shp 파일을 편집할 수 있는 (읽어 들일 수 있는)  프로그램을 찾는다.
2. 이 프로그램을 이용하여 SVG로 변환할 수 있는지 찾는다.
3. SVG로 변환된 정보를 svgto.jvectormap.com 에서 지도로 변환해 본다.

간단한거 같지만 이게 은근 쉽지가 않았다..
개발자로써 지도 프로그램을 쓰는 것도 그렇고 사용법을 일일이 찾아야 하므로... 

첫번째 사용했던 프로그램 FME Desktop 2013 이란 상용 프로그램이다.

14일간의 트라이얼을 기대하고 설치 했는데... 트라이얼 코드를 웹사이트에 접속해서 등록해야만 코드가 발급된다. ;;
여튼, 이 프로그램으로 좋은 것은.. SHP 파일을 정말 자세하게 까(?) 볼 수 있었다.

  1. FME Data Inspector

    shp 파일은 벡터 정보와 데이터 셋을 같이 가지고 있다. data inspector는 이 데이터 셋을 집중적으로 깔 수 있는 툴이다.

    아래 이미지는 data inspector를 이용하여 kor_adm2.shp 파일을 열었을때의 화면..



  2. FME Universal Viewer
    이 툴은 shp 파일의 벡터 부분을 집중적으로 깔 수 있다. 라인과, 포인트, 텍스트, 채우기 색깔등을 볼 수 있는 것.
    그리고 각 조각을 선택하면 그에 대한 정보를 보여준다. (사실 inspector와 거의 같은 정보..)
    이미지는 생략하고..

    이 툴을 이용해서 shp -> svg로 변환이 가능하다.
    shp가 그린 벡터 조각이 가진 어트리뷰터를 모드 svg path 태그안의 어트리뷰트로 변환이 된다.

    그런데!! 이걸 svgto.jvectormap.com 에서 지도로 변환해보면 지도가 안나온다.

    왜인지 모르겠는데 안나온다...

     
  3. FME Workbench
    이 툴은 전문가 적인 작업이 가능하다.

    내가 불러들여 작업할 파일을 설정하는데 이를 Reader라 하고
    어떤식으로 변환할 것인지를 설정해야하는데 이를 Writer라고 부른다.

    아래는 이에 대한 이미지..


    이렇게 리더와 Writer를 설정하고 OK 누르면 아래 이미지와 같은게 나온다..


    Writer 쪽 ... 을 눌러보면 User Attribute 탭 에서 내가 추출하고자 하는 Reader의 Attribute를 지정할 수 있다.
    Reader와 같은 이름을 지정해야만 SVG로 변환 할 시 나타나며 이름이 틀릴경우 나타나지 않는다.



    대충 설정을 하고 Shift + F5 를 누르게 되면 Reader -> Writer로 변환을 하게 되는데 jVectorMap으로 안나온다.

     
FME Desktop 2013 결론은 jVectorMap에서 사용하는 SVG를 추출할 수 없다.

내가 못하는 거 일 수 있지만 안된다.  그래서 다음 프로그램으로 패스...


두번째 사용한 프로그램은 무료로 사용할 수 있는 Quantum GIS 이다.
Quantum GIS : http://www.qgis.org/
한글 언어를 지원하는 프로그램으로 별 어려움 없이 사용 할 수 있었다.

설치 후 프로그램을 실행시켜 shp를 읽어들이고자 할때는 아래 아이콘을 이용한다. 


벡터 레이어를 추가하여 데이터 셋을 읽어들이면 되는데 사용하고자 하는 shp 파일을 로드하면 된다.

이제 SVG로 변환을 하여야 하는데 기본적인 기능에서는 제공되지 않는다.

그래서 플러그인을 설치하여야 한다. 

메뉴에서 플러그인 -> Python 플러그인을 눌러 플러그인 설치 관리자를 실행 시키고 필터에 Simple SVG 로 검색한다.
하나 밖에 없으므로 눌러서 플러그인 설치를 하면 SVG로 변환할 준비 끝.

설치가 완료되면 플러그인 툴바가 프로그램 하단에 생성된다.
설치된 SVG 플러그인을 누르면 창이 뜨는데 창 가운데 Resize Map or Set Extent를 눌러서 크기를 조절해야한다.




위 이미지에서 처럼 Width와 Height의 화살표를 눌러 값을 바꾸고 우측의 Set을 누르면 지도 전체 크기가 변경된다.
그리고 꼭 !!  빨간색 상자로 표시된 버튼을 눌러 벡터 이미지도 지도 크기에 맞게 사이즈 조절을 해야만 한다.
(창을 닫지 않고도 눌러 지므로 창 닫지는 말자..)

빨간색 상자 버튼을 누르고 난 후 아래 Set Extent 의 Set 버튼을 눌러 표시된 지도의 X,Y를 설정하자.
(누르면 자동으로 맞춰진다.)

OK를 누르고 첫 창으로 돌아와 Browse를 눌러 저장하고자 할 경로와 파일명.svg 를 적어주고 OK를 누르면 변환이 완료된다.

이 변환된 파일을 에디트플러스나 다른 편집 프로그램으로 열어 아래 사이트에서 실제 지도로 변환되는지 확인해보자.

here > http://svgto.jvectormap.com/ 


아래는 변환된 결과. 


구멍 하나 없이 제대로 나온다. 

그런데!! 이 Quantum GIS를 이용하여 SVG로 변환할 경우 Shape가 가지고 있는 attribute를 같이 변환하지 않는다.
Simple SVG는 레이어 단위로 변환을 하기 때문에 id와 name이 따라가려면 레이어로 다 쪼개야하는데... 못하겟다;;

여튼 저 화면에서 Map name과 지역별 id, name을 설정하고 save를 누르면 자바 스크립트 파일로 다운로드가 가능하고
이를 jVectorMap에서 사용할수 있다. 


이제 어떤 파일이든지 Quantum GIS 만 있으면 jVectorMap에서 사용할 수 있다!!
그러나 이름은 손으로 다 입력해야한다. 


SVG Path만 있다면 jVectorMap으로 가능하다. 

아래는 SVG 샘플...  








위 샘플을 아래 사이트에서 확인할 수 있다.

svg to jvectormap : http://svgto.jvectormap.com

준비물... 

1. python 2.7 이상 
3. GDAL
2. OSGeo4W


python이야 설치 파일 받아서 설치하면 되므로 간단하다. 정말..

GDAL 또한 아래 사이트에서 자기에 맞는 버전 찾아서 적당한 위치에 압축해제 하면 된다.
donwload : http://download.osgeo.org/gdal/win32/1.5/ 

해제 후 시스템 변수 중 path에 압축해제 한 폴더를 추가 해 주어야 하고.. 
압축 해제 폴더가 gdalwin32-1.7 이라면...  path에 C:\gdalwin32-1.7\bin 를 추가..
 

JAVA_HOME 을 설정할때와 마찬가지로..

Name : GDAL_DATA
Value : C:\gdalwin32-1.7\data

를 넣어주자.. 

이제 필요 한것은  Shapely 아래 사이트에서 다운로드 한 후 압축해제하고... 
downlaod : https://pypi.python.org/pypi/Shapely/1.2.16 

커맨드 창으로 python setup.py install 해주면 된다.

마지막으로.. 필요 한 것이 ogr 패키지 인데... 이건 OSGeo4W를 설치하면 쉽게 해결 할 수 있다.
download : http://trac.osgeo.org/osgeo4w/ (관리자 권한으로 설치 할 것)

installer를 다운받아 설치를 시작하면 몇 가지 플러그인을 설치할 것이냐고 묻는데 아무거나 해도 관계없다.. 

다 설치하게 되면 준비 끝.

shp 파일이 준비되었으면 cmd 창으로 converter.py를 실행시킬것이지만... 에러가 난다.. 분명.. 

시작을 누른후 OSGeo4W 라고 입력하면 프로그램을 실행 시킬 수 있다.
이 쉘을 실행 시킨 후 converter.py 를 실행시키면 아무 문제 없이 실행 시킬 수 있다.

그리고 피씨가 정말 후지지 않는 이상 컨버팅 작업 중 멈추지 않을 것이다..

아래처럼 대충 이렇게... 

python ../converter.py ^

--width 900 ^

--country_name_index 6 ^

--country_code_index 5 ^

--where "ISO = 'KOR'" ^

--name korea ^

--buffer_distance -0.4 ^

--simplify_tolerance 50 ^

KOR_adm2.shp ^

korea-map.js















 
OS : Ubuntu 12.0.4

jVectorMap을 개발자 버전으로 다운로드 하게 되면 convert.py 파일이 존재한다.

이 파일을 이용해서 shp 파일을 jVectorMap에서 사용가능하게끔 변환이 가능한데 이를 해야만 했었다......

우선 Ubuntu에 Python2.7.5를 설치해야 하는데 아래 의존성 관계를 먼저 해결해 주었어야 했다.
sudo apt-get install build-essential
sudo apt-get install libreadline-dev libncursesw5-dev libssl-dev libsqlite3-dev tk-dev libgdbm-dev libc6-dev libbz2-dev 
이후 home 디렉토리에 Python 2.7.5 를 wget으로 다운로드 받고 
wget http://python.org/ftp/python/2.7.5/Python-2.7.5.tgz
압축 풀고 인스톨
tar -xvf Python-2.7.5.tgz 
cd Python-2.7.5/
./configure
make
sudo make altinstall  

jVectorMap에서 사용하는 converter.py는 아래 두 개의 패키지가 추가로 필요하다.

삽질 시작.. 

 Shapely의 간편 설치를 위해 pip를 설치한다.

sudo apt-get install python-pip

pip install Shapely  

GDAL은 easy_install 을 사용한다.
sudo easy_install GDAL 
에러난다.. 아...
GDAL이 easy_install로 설치가 안된다. easy_install은 wget으로 파일을 받아와서 작업을 하는 방식이기에 wget으로도 안된다.
 
결국 아래 방법으로.. 진행

sudo add-apt-repository ppa:ubuntugis/ubuntugis-unstable sudo apt-get update sudo apt-get install gdal


안된다..... 레파지토리가 깨진듯하다..  방법이 없는건 아닌듯하다..

svn checkout https://svn.osgeo.org/gdal/trunk/gdal gdal ./configure sudo make install 

설치가 된다.. 근데 엄청 오래 걸린다........ 백그라운드로 실행 안한걸 후회할 정도로... 30분정도..

이렇게 설치하면 계정 Path에 설정해 주어야 하는데..

echo " " >> $HOME/.bashrc echo "# GDAL environment variables" >> $HOME/.bashrc echo "export LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH" >> $HOME/.bashrc source $HOME/.bashrc

gdal 설치하다가 시간 다 보낸듯..
맵 파일 넣고 convert를 해보니..

Traceback (most recent call last): File "converter.py", line 10, in <module> from osgeo import ogr ImportError: No module named osgeo

이런 에러 나온다...
찾아보니..
http://gis.stackexchange.com/questions/64398/importing-osgeo-module-in-ubuntu-10-04
다시 gdal... configure 부터...

./configure --with-python sudo make install 



이상 여기까지 삽질.... 2시간..

sudo apt-get install python-gdal sudo apt-get install python-shapely 


위 두개면 설치 된다.... 아..
설치 다 했으니 아래 처럼 실행을 하면...

python ../converter.py \ --width 1000 \ --country_name_index 7 \ --country_code_index 8 \ --where "ISO = 'KOR'" \ --input_file_encoding 'UTF-8' \ --name datag_korea \ --language en \ KOR_adm2.shp \ jquery-jvectormap-data-korea-aea-en.js


이런 에러가 나는데..

ERROR 6: Unable to load PROJ.4 library (libproj.so), creation of OGRCoordinateTransformation failed.


아래 처럼 설치해주고..

sudo apt-get install python-pyproj


확인도 해주어야 하고..

/sbin/ldconfig -p | grep proj libproj.so.0 (libc6,x86-64) => /usr/lib/libproj.so.0 

 심링크도 잡아 주어야 한다....

sudo ln -s /usr/lib/libproj.so.0 /usr/lib/libproj.so


확실하게 이것도 설치하자..

sudo apt-get install python-anyjson

 다시.. 실행하자....  

python ../converter.py \ --width 1000 \ --country_name_index 7 \ --country_code_index 8 \ --where "ISO = 'KOR'" \ --input_file_encoding 'UTF-8' \ --name datag_korea \ --language en \ KOR_adm2.shp \ jquery-jvectormap-data-korea-aea-en.js

  아래는 결과..

vi로 열어보면...

올ㅋ


근데 안된다.....
jVectorMap에서 권장하는 Natural Earth 이곳의 데이터를 기준으로 작업할 경우 지도가 올바른 형태로 나오는데
국가별로 지도를 뽑으려고 해보니 뭔가가 안맞다...
같은 shp 파일이라도 뭔가가 다르다..? 열어봐야 아는건데.. 
급한대로.. 다른 개발자가 작업한 곳을 찾아서 사용은 한다만...



안되는 이유는.. 작업이 죽는다..;;

행정동 레벨 2기준으로 작업을 할 경우 약 200여개의 행정동(시/군/구)가 나오는데 가상서버가 힘들어하더니 작업을 멈추네..

WIndow에서 아래처럼 지도는 나옴. 

 

Sample Data

data.js



 

Result

Source

	function numberFormatComma(n) {
		var reg = /(^[+-]?\d+)(\d{3})/;   // 정규식
		n += '';                          // 숫자를 문자열로 변환
		while (reg.test(n)){
			n = n.replace(reg, '$1' + ',' + '$2');
		}
		return n;
	}
	
	var countryData = []; 
	//for each country, set the code and value
	$.each(data.countries, function() {
	    countryData[this.code] = this.pGdp;
	});
	
	var markers = [ 
		{
			latLng : [ 41.90, 12.45 ],
			name : 'Vatican City',
			type : 'eu',
			style: {r: 15},
			population : 800,
			url : 'http://www.vatican.va/phome_en.htm'
		}, {
			latLng : [ 48.856614, 2.352222 ],
			name : 'Paris',
			type : 'eu',
			population : 2229385,
			url : 'http://www.paris.fr/english'
		}, {
			latLng : [ 37.60, 127.00 ],
			name : 'Seoul',
			type : 'asia',
			population : 10442426,
			url : 'http://www.seoul.go.kr'
		} 
	];
	
	var colors = [];
	for (var i = 0; i < markers.length; i++) {
	    if (markers[i].type == 'eu') {
	        colors[i] = 0;
	    }
	    else {
	        colors[i] = 1;
	    };
	};
	
	$(document).ready(function(){
		// map
		$('#jvm_worldMap').vectorMap({
			map : 'world_mill_en',
			series : {
				regions : [ {
					values : countryData,
					scale : [ '#C8EEFF', '#0071A4' ],	// two colors: for minimum and maximum values 
					normalizeFunction : 'polynomial'
				} ]
				,
				markers: [{
		            attribute: 'fill',
		            scale: ['#1B77E0', '#E01B1B'],
		            values: colors,
		            min: 0,
		            max: 1
		        }]
			},
			//-----------------------------------------------
			// changed to onRegionLabelShow from onLabelShow
			//-----------------------------------------------
	        	onRegionLabelShow: function(e, el, code) {
		            //search through data to find the selected country by it's code
		            var country = $.grep(data.countries, function(obj, index) {
		                return obj.code == code;
		            })[0]; //snag the first one
		            //only if selected country was found in dataC
		            if (country != undefined) { 
				el.html(el.html() + 
	                        "
Code: " +country.code + "
Name: " + country.name + "
GDP/Person: $ " + numberFormatComma(country.pGdp)+ "
Population: "+ numberFormatComma(Math.round(Number(country.pop)/10000))+" 만명"); } } ,markerStyle : { initial : { fill : '#F8E23B', stroke : '#383f47' } }, markers : markers, onMarkerClick: function(events, index) { if(confirm('확인을 누르시면 홈페이지로 이동 합니다.')){ window.open(markers[index].url, markers[index].name); } }, onMarkerLabelShow: function(e, el, code) { var msg = ""+el.html()+""; var source = markers[code]; msg += "
population : "+numberFormatComma(source.population)+" 명"; el.html(msg); } }); });


HTML


+ Recent posts