해당 포스팅에서는 상단 이미지 예시와 같이, 풍속 값을 기반으로 하여 바람 깃 자료를 svg형태로 그려내고 풍향 값을 참고하여 해당 svg를 회전시켜 wind barb를 highcharts 내 표출하는 방법에 대해 다루었다.
목차는 다음과 같이 하였다.
1. wind barb 정의
1) 바람 깃 svg 처리 메서드
2) svg 적용 메서드
3) 로드 예시
2. highchart 표출
3. 구현 예시
1. wind barb 정의
바람 깃 생성을 위해 연관 작업을 처리하는 메서드를 3개로 나누어 작업하였다. 아래 목록화 된 목차 제목이 각 메서드 대한 내용이며, 연계처리하여 load메서드 실행->바람 깃 생성->바람 깃 svg 생성 순으로 호출되도록 하였다. 즉, 차트 객체 정의 혹은 차트 생성 시, 해당 차트에서 정의한 load메서드를 실행하면 wind barb를 그려낼 수 있다. 더 자세한 내용은 목차 순으로 상세하게 정리하였다.
function Meteogram() {} //로직 정의 전, 생성자 함수 필수
1) 바람 깃 svg 처리 메서드
아래 메서드는, 풍속 값을 전달 받아 값에 해당하는 모양으로 svg값을 path배열 객체로 반환하는 로직이다. 우선 주석과 함께 아래 코드를 훑어본 뒤 밑 단락에서 추가적인 설명을 첨부하겠다. iswindprofiler인자의 경우는 본인의 경우 차트 타입에 따라 바람깃의 표출을 상이하도록 처리하기 위해 추가한 인자이므로, 필요에 따라 생략해도 무관하다.
Meteogram.prototype.windArrow = function(speed, iswindprofiler) {
var scale = 1.2; // 크기 배율
var path = [];
if(!iswindprofiler){ //머리포함
path = [
'M', 0, 7* scale,
'L', -1.5* scale, 7* scale,
0, 10* scale,
1.5* scale, 7* scale,
0, 7* scale,
0, -10* scale
];
}else{// 머리 미포함
path = [
'M', 0 * scale, 7 * scale, // 줄기 시작점
'L', 0 * scale, -10 * scale // 줄기 끝점
];
}
speed=Math.round(speed)/1.944 //반올림한 풍속을 kt to m/s단위로 변환
if (speed <= 1) {
path = []; // 바람 속도가 낮을 경우 아무것도 그리지 않음
} else if (speed < 5) {
path.push('M', 0 * scale, -8 * scale, 'L', 4 * scale, -8 * scale); // 짧은 선
} else if (speed < 7) {
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if (speed < 10) {
path.push('M', 0 * scale, -7 * scale, 'L', 4 * scale, -7 * scale); // 짧은 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if (speed < 12.8) {
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -7 * scale); // 긴 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if(speed < 15){
path.push('M', 0 * scale, -4 * scale, 'L', 4 * scale, -4 * scale); // 짧은 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -7 * scale); // 긴 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if(speed < 18){
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -4 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -7 * scale); // 긴 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if(speed < 20){
path.push('M', 0 * scale, -1 * scale, 'L', 4 * scale, -1 * scale); // 짧은 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -4 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -7 * scale); // 긴 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if(speed < 23){
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -1 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -4 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -7 * scale); // 긴 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if(speed < 25.5){
path.push('M', 0 * scale, 2 * scale, 'L', 4 * scale, 2 * scale); // 짧은 선
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -1 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -4 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -7 * scale); // 긴 선
path.push('M', 0 * scale, -10 * scale, 'L', 7 * scale, -10 * scale); // 긴 선
} else if(speed < 28){
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 30.5){
path.push('M', 0 * scale, -4 * scale, 'L', 4 * scale, -7 * scale); // 짧은 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 33.4){
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -8 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 36){
path.push('M', 0 * scale, -1 * scale, 'L', 4 * scale, -4 * scale); // 짧은 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -9 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 38.5){
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -6 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -9 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 41){
path.push('M', 0 * scale, 2 * scale, 'L', 4 * scale, -1 * scale); // 짧은 선
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -6 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -9 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 43.5){
path.push('M', 0 * scale, 2 * scale, 'L', 7 * scale, -3 * scale); // 긴 선
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -6 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -9 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
} else if(speed < 46){
path.push('M', 0 * scale, 5 * scale, 'L', 4 * scale, 2 * scale); // 짧은 선
path.push('M', 0 * scale, 2 * scale, 'L', 7 * scale, -3 * scale); // 긴 선
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -6 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -9 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
}
else{
path.push('M', 0 * scale, 5 * scale, 'L', 7 * scale, 0.5 * scale); // 긴 선
path.push('M', 0 * scale, 2 * scale, 'L', 7 * scale, -3 * scale); // 긴 선
path.push('M', 0 * scale, -1 * scale, 'L', 7 * scale, -6 * scale); // 긴 선
path.push('M', 0 * scale, -4 * scale, 'L', 7 * scale, -9 * scale); // 긴 선
path.push('M', 0 * scale, -7 * scale, 'L', 7 * scale, -12 * scale, 'L', 0 * scale, -10 * scale) //삼각형
}
return path;
};
scale객체의 값을 부여하여 바람 깃 아이콘의 사이즈를 일괄적으로 조절할 수 있도록 하였다. 본인의 경우 풍속 값의 단위가 작업 지시 사항 전달 과정에서 kt, m/s등으로 계속해서 변동이 있었기 때문에 부득이하게 kt단위로 전달된 풍속 값 인자를 m/s단위로 변경하여 해당 단위에서의 분기 값을 기준으로 하여 바람 깃 아이콘의 모양을 다르게 처리하였다.
바람 깃의 풍속 값 별 모양 예시를 아래 첨부하였다.
https://blog.mapbox.com/mapping-wind-barbs-to-show-speed-and-direction-4d3078add03d
2) svg 적용 메서드
인자로 전달된 highcharts객체 값을 기반으로 하여 위 로직을 통해 값으로써 생성한 svg아이콘을 정의한 스타일에 맞추어 차트 내 표출되도록 그려내는 기능을 하는 메서드이다. 추가 설명이 필요한 만큼의 복잡도는 아니기 때문에 이외 설명은 주석으로 대체하였다.
Meteogram.prototype.drawWindArrows = function(chart, iswindprofiler) {
var meteogram = this;
var dataSize = 1; // 모든 포인트에 표시하거나 간격을 조정
var xLineHeight='';
if(!iswindprofiler){
xLineHeight = chart.plotHeight;//차트 바닥에 정렬
}
var speedData = chart.series[0].data.map(function(point) {
return point.speed; // 데이터 포인트에 'speed' 속성이 있다고 가정
});
var directionData = chart.series[0].data.map(function(point) {
return point.direction; // 데이터 포인트에 'direction' 속성이 있다고 가정
});
var colorData = chart.series[0].data.map(function(point) {
return point.color; // 데이터 포인트에 'color' 속성이 있다고 가정
});
$.each(chart.series[0].data, function(i, point) {
var arrow, x, y;
if (i % dataSize === 0) {
x = point.plotX + chart.plotLeft;
if(!iswindprofiler){
y = xLineHeight; //차트 바닥에 정렬
}else{ y = point.plotY; }
if (speedData[i] <= 1) {
arrow = chart.renderer.circle(x, y, 3).attr({
fill: 'none'
});
} else {
arrow = chart.renderer.path(
meteogram.windArrow(speedData[i], iswindprofiler)
).attr({
rotation: parseInt(directionData[i], 10),
translateX: x,
translateY: y
});
}
arrow.attr({
stroke:colorData[i],
fill:colorData[i],
'stroke-width': 1.5,
zIndex: 5
}).add();
}
});
};
3) 로드 예시
해당 아래 메서드는 단순히 drawWindArrows메서드를 실행시킬 목적의 함수이다. 별도 메서드로 분류한 이유는, 혹여 차트 로드 시 추가적으로 작업이 필요하여 다른 동작을 수행해야할 경우 해당하는 동작을 함수화 하여 load함수 내에서 한 번에 호출하여 동작시킬 수 있도록 후일의 유지보수를 용이하도록 처리하기 위함이다.
Meteogram.prototype.onChartLoad = function(chart, iswindprofiler) {
this.drawWindArrows(chart, iswindprofiler);
};
하단 링크는 highcharts에서 제공하는 바람 깃 생성 데모이다.
https://www.highcharts.com/demo/highcharts/windbarb-series
2. highchart 표출
사용 방법은 highcharts 객체의 chart.events.load 시, Meteogram.prototype.onChartLoad가 실행될 수 있도록 하면 된다.
chart: {
type: 'scatter', //차트 타입
events: {
load:function() { meteogram.onChartLoad(this, true); } //활용 예시
}
},
3. 구현 예시
아래 코드는 위 내용들을 종합하여 code pen으로 구현해낸 예시이다. 주석 내용으로 상세 흐름 이해 가능하다.
See the Pen highcharts wind barb by HamJeong (@HamJeong) on CodePen.
구현 예시와 더불어 ajax활용하여 시리즈 데이터를 비동기로 반환받고, 차트 내용을 업데이트 할 수 있도록 처리하는데에 활용한 코드를 일부 첨부하였다.
$.ajax({
type: "get",
url: "url",
dataType: "json",
data: { //전송 데이터 예시
stnCd: $("#stnCd").val(),
tm: $("#tm").val(),
view: $("#view").val(),
altitude: $("#altitude").val()
},
success: function(data) {
// 1. 차트 객체 값 조정
const leftChartData = data[Object.keys(data)[0]];
windprofilerLeftChartsOptions.title.text = Object.keys(data)[0]; // 첫 번째 객체의 키
windprofilerLeftChartsOptions.yAxis.max = parseFloat($("#altitude").val()); //최대 표출고도
windprofilerLeftChartsOptions.xAxis.categories = leftChartData.xAxis; // xAxis 데이터
windprofilerLeftChartsOptions.series[0].data = leftChartData.data.map(function(item) {// 첫 번째 시리즈 데이터
return {
x: parseFloat(item.x), // x 값을 숫자로 변환
y: parseFloat(item.y), // y 값을 숫자로 변환
speed: parseFloat(item.speed), // 속도
direction: parseFloat(item.direction), // 방향
color: item.color // 색상
};
});
// 2. 조정된 객체로 차트 업데이트
Highcharts.chart('windprofiler_content_1', windprofilerLeftChartsOptions);
},
error: function(xhr, status, error) {
console.log(error)
}
});
highcharts에서 제공하는 AI 챗 서비스 링크를 첨부하였다. 국문으로 문답해서인지 답변 정확도는 떨어지는 편이지만, 간편한 동작 하나를 위해 전체 데모를 확인하고, 개발자 문서를 탐색하는 것 보다 빠르게 원하는 내용을 확인할 수 있다.
https://www.highcharts.com/chat/gpt/
'JavaScript > JQuery' 카테고리의 다른 글
[zoomer.js] 이미지 확대 축소 라이브러리 활용 (0) | 2024.11.21 |
---|---|
[tooltip] JQuery-ui.js, 요소 tooltip 생성부터 커스텀까지 (4) | 2023.12.07 |
AJAX로 json데이터 넘겨받기 (0) | 2022.08.04 |
타임슬라이더 구현 (0) | 2022.06.24 |
요소의 조작 : 제이쿼리 (0) | 2022.05.31 |