흐름 방향을 갖는 선형 설비에 대한 심벌 표현

안드로이드 기반의 GIS 엔진인 BlackPoint-Xr은 현장에서 내 위치 주변의 설비를 조회하고 정상 여부를 검사하는 앱 개발을 한 기반이 됩니다. 설비 중 전력선, 상수/하수관로, 가스관 등은 선형으로 표시되는데, 이때 흐름의 방향이 사용자에게 중요한 의미를 갖게 됩니다. 아래의 화면은 BlackPoint-Xr을 이용해 개발된 하수관망 현장조회 시스템의 화면중 하나입니다.

하수관거 중 합류, 오수, 차집관 등에 대해 물의 흐름을 화살표로 표시하여 사용자가 직관적으로 파악할 수 있습니다.

BlackPoint-Xr에서 이러한 선형 설비에 대한 화살표 표시에 대한 심벌 표시에 대한 API에 대해 정리하겠습니다.

BlackPoint-Xr은 선형 설비에 대한 심벌을 StrokeSymbol이라는 타입으로 정의합니다. 아래의 코드는 선형 설비에 대한 레이어의 StrokeSymbol 객체를 인자로 넘겨주면 선형의 형상을 연속된 화살표로 표시해 주는 SetFlowLineStyle 함수 코드입니다.

private void SetFlowLineStyle(Resources res, StrokeSymbol symbol) {
    float width = Utility.px(res, 10);
    float height = Utility.px(res, 10);
		
    Path dash = new Path();
    dash.moveTo(0, -height/4); 
    dash.lineTo(width/2, -height/4); 
    dash.lineTo(width/2, -height/2); 
    dash.lineTo(width, 0); 
    dash.lineTo(width/2, height/2); 
    dash.lineTo(width/2, height/4); 
    dash.lineTo(0, height/4);
		
    PathDashPathEffect pathdash = new PathDashPathEffect(dash, width + width*0.2f, 0, PathDashPathEffect.Style.ROTATE);
		
    symbol.getPaint().setPathEffect(pathdash);
}

위의 함수는 2개의 인자를 받는데요. 첫번째는 연관된 Activity의 getResources()로부터 쉽게 얻을 수 있는 객체인 res 인자와 앞서 언급한 StrokeSymbol 타입의 symbol입니다. res 인자가 필요한 이유는 연속된 화살표의 크기를 단말기의 해상도나 dpi와 상관없이 일정한 크기로 표시되도록 하기 위함인데.. 3번과 4번의 width와 height을 얻기 위해 사용됩니다. 이 width와 height는 연속된 화살표를 구성하는 단일 화살표의 너비와 높이에 대한 Pixel 값입니다. Utility.px 함수는 다음과 같습니다.

public static float px(Resources res, float dp) {
    if(_density < 0) {
	_density = res.getDisplayMetrics().density;
    }

    float px = dp * _density;
		
    return px;
}

즉, 위의 px라는 정적 함수는 dp 단위를 px 단위로 변환해 줍니다. 아시겠지만, 안드로이드에서 레이아웃이나 그래픽 요소의 표현 단위는 최대한 px가 아닌 dp로 해줘야 합니다.

다시 SetFlowLineStyle 함수로 돌아가서 6번~13번 코드가 화살표의 형상을 구성하는 코드입니다. 즉, 앞서 구한 width와 height 값을 통해 화살표 모양의 Path를 구성하게 되는데요. 패스의 구성 좌표의 지정은 아래의 화면을 참고하면 쉽게 이해할 수 있습니다.

이렇게 만들어진 Path 객체를 14번 코드에서 PathDashPathEffect 객체의 생성에 사용하고 이를 StrokeSymbol의 Paint 객체에 설정해 주기만 하면 됩니다.

PostgreSQL의 PL/pgSQL 튜토리얼 – 5 : CASE 조건문

안녕하세요, GIS Developer 김형준입니다. 이번 포스트에서는 조건문 중 CASE 문에 대해 살펴 보겠습니다. CASE 문의 설명을 위해 실제 DBMS에 저장된 Table을 활용해 설명하겠습니다. 사용할 테이블은 다음과 같습니다.

PERSON이라는 이름의 테이블에 총 5개의 Row가 저장되어 있는 간단한 데이터입니다.

CASE 문의 설명을 위해 CASE를 활용한 아래와 같은 함수를 만들어 보겠습니다.

CREATE OR REPLACE FUNCTION get_surname(part_name CHAR(20)) 
RETURNS CHAR(2) AS $$
DECLARE 
    full_name CHAR(20);
    surname CHAR(1);
BEGIN
    SELECT INTO full_name name
    FROM person
    WHERE name LIKE '%' || part_name || '%';
    
    IF full_name IS NOT NULL THEN
        surname = substr(full_name, 1, 1);
        
        CASE surname
        WHEN '김' THEN
            RETURN '金氏';
        WHEN '이' THEN
            RETURN '理氏';
        WHEN '박' THEN
            RETURN '博氏';
        WHEN '최' THEN
            RETURN '崔氏';
        ELSE
            RETURN '모름'; 
        END CASE;
        
    	RETURN surname;
    ELSE
		RETURN NULL;
	END IF;
END; $$
LANGUAGE plpgsql;

위의 함수에 대한 코드 중 14번에 CASE 조건절이 보입니다. surname 변수에는 조회된 이름의 성에 대한 1개의 문자가 할당됩니다. 이 문자에 따라 한자로 그 결과를 반환해 주게 되는데요. 14번의 CASE에 조건과 비교할 대상 값을 담고 있는 변수를 지정하고, 15번과 17번 등의 WHEN에 일치하는 값을 지정합니다. 조건과 일치하는 WHEN을 만나면 WHEN의 밑에 존재하는 코드 부분이 실행됩니다. 다음은 실행 결과입니다.

위의 경우처럼 정확한 값의 일치에 대한 조건 비교가 아닌 어떤 값의 범위에 대한 조건 비교에도 CASE와 WHEN을 사용할 수 있습니다. 이에 대한 다음의 코드는 아래와 같습니다.

CREATE OR REPLACE FUNCTION get_age_level(part_name CHAR(20)) 
RETURNS CHAR(20) AS $$
DECLARE 
    the_age INTEGER;
BEGIN
    SELECT INTO the_age age
    FROM person
    WHERE name LIKE '%' || part_name || '%';
    
    CASE
        WHEN the_age < 10 THEN
            RETURN '어린아이';
        WHEN the_age >= 10 AND the_age < 20 THEN
            RETURN '10대 사춘기';
        WHEN the_age >= 20 AND the_age < 30 THEN
            RETURN '20대 취준생';
        WHEN the_age >= 30 AND the_age < 40 THEN
            RETURN '30대 청춘';        
        ELSE
            RETURN '불노장생';
    END CASE;
END; $$
LANGUAGE plpgsql;

get_age_level은 인자로 지정한 이름과 유사한 Row의 age 필드값의 범위에 따라 각기 그 나이대를 표현하는 문자열을 반환합니다. 이때 나이 범위에 대한 조건을 CASE WHEN 구문으로 분류하고 있습니다. 아래는 실행 결과입니다.