[GIS] 정규표현식을 이용한 SHP 추출, ExpressionsSHP

개발자 피드백 : 포인트 타입의 SHP 파일에 대해 문제가 발생하는 것을 해결 하였습니다.

SHP 파일의 속성값을 기준으로 원하는 값과 일치하는 도형만을 추출해 새로운 SHP 파일로 추출해 내는 툴입니다. 일치하는 속성값의 기준은 정규표현식을 사용합니다.

지적도를 예로 들어 JIBUN이라는 필드값이 ‘도’로 끝나는 모든 도형을 추출하라는 정규 표현식은 ‘도$’가 됩니다. 참고로 ‘도’로 끝나는 경우 도로에 해당합니다. 아래의 실행 화면은 이와 같은 예에 대한 실행 예입니다.

사용자 삽입 이미지
보시는 것처럼 입력 SHP 파일을 지정하고 대상이 되는 필드, 정규표현식값.. 끝으로 조건과 일치하는 도형을 저장할 새로운 SHP 파일명을 지정하면 됩니다. 위의 실행에 대한 결과는 아래와 같습니다.

사용자 삽입 이미지
보시는 것처럼.. 도로만이 추출된 결과를 볼 수 있습니다. 이 프로그램은 듀라맵(http://www.gisdeveloper.co.kr/notice/574)을 이용해 개발되었으므로 듀라맵을 PC에 등록해줘야 합니다. 이 프로그램에 대한 소스 코드와 실행 파일을 아래 링크를 통해 다운받을 수 있습니다.

정규표현식을 통해 원하는 값을 선택해 내는 일은 매우 유연한 방법입니다. 원하는 경우와 상황에 대한 정규표현식에 대해서 댓글을 통해 알려주시면 성의껏 답변해 드리겠습니다.

[iOS] 바이너리 파일(Binary File) 쓰고 읽기

iOS에서 바이너리 파일을 생성하고 정수, 실수 그리고 문자열 값을 순서대로 쓴 후에 다시 읽기 위한 코드에 대해 정리해 봅니다. Objective-C에서 제공하는 파일 관련 클래스를 사용을 시도했으나 Ansi-C에서 제공하는 파일처리 함수를 사용했습니다. 진정 ‘구관이 명관’입니다 !!

UI 상에 정수, 실수 그리고 문자열을 입력받는 텍스트필드 컨트롤을 추가하여 UI를 구성합니다. 그 화면은 다음과 같습니다.
사용자 삽입 이미지
여기서 Save 버튼을 클릭했을때 입력된 값들을 바이너리 파일에 저장하는 코드 예는 다음과 같습니다.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentDir = [paths objectAtIndex:0];
    NSString *file = [documentDir stringByAppendingPathComponent:@"mydata.bin"];
    const char *szFileName =  [file UTF8String];
    FILE *pFile = fopen(szFileName, "wb");
    if(pFile != NULL) {
        int intValue = [tiInteger.text intValue];
        fwrite((void *)&intValue, sizeof(intValue), 1, pFile);
        
        float floatValue = [tiFloat.text floatValue];
        fwrite((void *)&floatValue, sizeof(floatValue), 1, pFile);

        NSString *stringValue = tiString.text;
        const char *bytesStringValue = [stringValue UTF8String];
        int lenStringValue = strlen(bytesStringValue) + sizeof(int);
        fwrite((void *)&lenStringValue, sizeof(lenStringValue), 1, pFile);
        fwrite((void *)bytesStringValue, sizeof(char), lenStringValue, pFile);
        
        fclose(pFile);
    }

iOS는 기본적으로 파일을 쓸 수 있는 디렉토리를 제한해 놓습니다. 파일을 쓸 수 있는 디렉토리를 얻기 위해 1번 ~ 5번 코드가 사용되었습니다. 뭐… 나머지는 fopen과 fwrite와 같은 파일 열기 및 쓰기에 대한 일반 C 함수를 사용했습니다.

다음은 UI 중 Restore 버튼을 클릭했을때 앞서 Save 버튼에 의해 저장된 값들을 복원하는 코드입니다.
    NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory,
                                                         NSUserDomainMask,
                                                         YES);
    NSString *documentDir = [paths objectAtIndex:0];
    NSString *file = [documentDir stringByAppendingPathComponent:@"mydata.bin"];
    const char *szFileName =  [file UTF8String];
    FILE *pFile = fopen(szFileName, "rb");
    if(pFile != NULL) {
        int intValue;
        fread((void *)&intValue, sizeof(intValue), 1, pFile);

        float floatValue;
        fread((void *)&floatValue, sizeof(floatValue), 1, pFile);
        
        int lenStringValue;
        fread((void *)&lenStringValue, sizeof(lenStringValue), 1, pFile);
        
        NSMutableData *data = [NSMutableData dataWithCapacity:lenStringValue];
        char *bytesStringValue = (char *)[data bytes];
        fread((void *)bytesStringValue, lenStringValue, 1, pFile);
        
        fclose(pFile);
        
        tiInteger.text = [NSString stringWithFormat:@"%d", intValue];
        tiFloat.text = [NSString stringWithFormat:@"%f", floatValue];
        tiString.text = [NSString stringWithUTF8String:bytesStringValue];
    }

저장했던 값들에 대해서 순서대로 읽어 오고 있습니다. 주의할 점은 문자열 값을 읽기 위해서 char 배열을 생성하기 위해 18번 ~ 19번 코드처럼 NSMutableData 클래스를 사용했습니다. Objective-C는 new 연산자를 지원하지 않습니다 !!

“위안부 강제동원 증거 없다”

사용자 삽입 이미지
한 나라의 수장이라는 총리라는 놈이.. 한다는 소리가 고작.. “증거 없다”라니.. 니가 말하는 그 증거 찾을라치면 한없이 찾을 수 있을 건데.. 도대체.. 어제 무슨 술을 쳐먹고 이런 소릴 하는것인가..
 
생각 좀 하고 떠들어라.. 너의 그 말 한마디가 결국 너의 손과 다리를 묶고.. 네가 대표하는 한 나라를 곤란한 지경에 치닫게 되리라는 걸 모르는거냐..

하긴.. 아니되면 ‘총리사퇴’가 이미 익숙해져 버린 네 놈들 나라 풍습이지만 말이다.. 니들 후손을 생각한다면 어찌 이런 말로 거짓됨을 마치 사실인것 처럼 씨부렁댄단 말이냐.. 일본, 너희는 어쩔 수 없는 ‘망국(亡國)’이다..

[iOS] 이미지 회전하기

iOS에서 이미지를 화면에 표시하는 방법 중 2가지에 대해 정리해 봅니다. 첫번째는 UIView를 상속 받아 drawRect 함수를 재정의해 이미지를 직접 그리는 방식, 두번째는 이미지를 표시하는 UIImageView 컨트롤을 이용하는 방식입니다.

먼저 첫번째 방식인 UIView를 상속받아 drawRect 함수를 재정의해 이미지를 그리는 방식입니다. 여타 다른 코드는 소스 코드에 대한 첨부 파일을 살펴보면 될 것이고… 중요한 drawRect 함수만을 살펴보면 다음과 같습니다.
- (void)drawRect:(CGRect)rect
{
    CGContextRef context = UIGraphicsGetCurrentContext();
        
    UIImage *img = [UIImage imageNamed:@"goon.png"];
    CGSize imgSize = [img size];

    static CGFloat angle = 0;
    angle += 5;
    CGFloat imgWidth = imgSize.width/1.5;
    CGFloat imgHeight = imgSize.height/1.5;
    CGFloat x = 80;
    CGFloat y = 120;
    
    CGRect rc = CGRectMake(x, y, imgWidth, imgHeight);
    
    CGContextTranslateCTM(context, imgWidth/2+x, imgHeight/2+y);
    CGContextRotateCTM(context, angle * M_PI/180);
    CGContextTranslateCTM(context, -imgWidth/2-x, -imgHeight/2-y);
    
    [img drawInRect:rc];
}

이미지 회전에 대해서 인지해야할 코드만을 설명드리면 8번의 angle 값이 회전값입니다. 그리고 12번과 13번 코드는 이미지를 표시할 화면상의 좌표입니다. 17번 ~ 19번 코드가 실제 이미지를 회전시키기 위한 코드로써 이미지를 중심으로 회전하기 위해 이동 변환과 회전 변환 다시 역이동 변환을 수행하고 있습니다. 이렇게 하면 다음처럼 화면에 이미지가 표시됩니다.

사용자 삽입 이미지
GooN이라는 버튼을 누르면 이미지가 5도씩 회전됩니다. 코드중 angle 변수를 static으로 지정한 이유가 바로 회전 각도 값을 유지하기 위함이였습니다.
이제 두번째로 UIImageView를 통해 이미지를 표시하고 회전해 보는 코드를 살펴보겠습니다. 일단 .xib 파일을 편집해 UI 단에 UIImageView 컨트롤을 원하는 위치에 배치하고 이 컴포넌트에 이미지 파일을 지정하면 쉽게 이미지를 표현할 수 있습니다. 이제 여기서 버튼을 클릭하면 이미지를 회전하는 코드를 살펴보면 다음과 같습니다.
-(IBAction)rotateAction:(id)sender
{
    [UIView beginAnimations:nil context:nil];
    [UIView setAnimationDuration:3];
    
    CGAffineTransform rotateTransform =
        CGAffineTransformMakeRotation(angle * M_PI / 180);
    
    angle = (angle == 180 ? 360 : 180);
    imageView.transform = rotateTransform;
    
    [UIView commitAnimations];
}

보시면 앞서 살펴본 첫번째 방법처럼 단순히 회전된 상태의 이미지를 화면에 찍는 것이 아닌 애니메이션 효과를 주어서 이미지를 회전시키고 있습니다. 이게 바로 UIImageView와 같은 컨트롤을 통해 이미지를 표현하는 가장 큰 장점입니다. 코드도 훨씬 간결하구요 !! 🙂 실행 결과는 첫번째에서 소개한 실행 화면과 동일하므로 생략하겠습니다. 차이점은 애니메이션을 통해 이미지가 회전된다는 것입니다.

끝으로 첫번째 방법과 두번째 방법에 대한 XCode 프로젝트에 대한 전체 소스 코드를 첨부합니다.

firstMethod.zip 파일이 UIView의 drawRect 함수의 재정의를 통해 이미지를 회전해 표시하는 것이고 secondMethod.zip 파일이 UIImageView 컨트롤을 이용해 이미지를 회전하는 것입니다.

Apache Commons IO 라이브러리

아파치 소프트웨어 재단에서 제공하는 자바 기반의 오픈소스 중  Commons IO 라이브러리가 있습니다. 얼마전 고객과의 미팅을 통해 요구사항을 파악하던 중.. 예상치 못한 기능을 요청받았고 이 기능을 기존 시스템에 추가하려면 상당한 코딩을 해야 할 상황에서 요긴하게 사용한 라이브러리 중에 하나입니다.

사용자 삽입 이미지
이  Commons IO 라이브러리는 기존의 JDK에서 제공하는 클래스들에 대해서 자주 사용하는(Commons 한) 기능들을 클래스화 해 놓은 것으로 이해하면 크게 틀리지 않습니다. 물론 기존의 JDK에서 제공되는 클래스들에 대해 보다 개선된 클래스를 제공하고 있기는 하지만 말입니다.

이 글은 Commons IO 라이브러리의 공식 홈페이지에서 매우 간단한 예제를 통해 해당 라이브러리를 소개하고 있는 글을 한글로 정리한 것입니다. 사실.. 제가 이 Commons IO를 직접 사용한것이 아니고 대용량 파일을 서버로 업로드하는 기능을 제공하는 아파치 소프트웨어 재단에서 제공하는 또 다른 Commons Fileupload 라이브러리에서 이 Commons IO 라이브러리를 사용하는 것을 보고 관심을 가져 보게 되었고 실제 기본적인 내용을 살펴보게 되었습니다.

먼저 해당 URL에 대한 데이터 전체를 문자열로 가져오는 예입니다.

InputStream in = null;
try {
    in = new URL("href='http://www.google.co.kr").openStream();
    System.out.println(IOUtils.toString(in));
} catch (MalformedURLException e) {
     e.printStackTrace();
} catch (IOException e) {
    e.printStackTrace();
} finally {
    IOUtils.closeQuietly(in);
}

JDK에서 제공하는 기본적인 클래스들을 이용해 위의 코드를 작성했다면 더 긴 코드가 필요했을테고.. 코드가 길다는 것은 그만큼 문제가 발생할 가능성이 높다는 것입니다. 아파치의 Commons IO는 수많은 프로그램에서 사용된 검증된 라이브러리입니다.

다음은 텍스트 파일을 라인별로 읽어 List 컨테이너로 파싱하는 예입니다.

File file = new File("c:/text.txt");
try {
    List lines = FileUtils.readLines(file, "utf-8");
    Iterator it = lines.iterator();
    while(it.hasNext()) {
        System.out.println(it.next());
    }
} catch (IOException e) {
    e.printStackTrace();
}

매우 직관적이고 심플합니다.. 하지만 대용량의 텍스트 파일의 경우 모든 데이터를 읽어 하나의 리스트 컨테이너에 집어 넣으니 메모리 문제가 발생할 수 있다는 점을 염두해 둬야 합니다.

다음은 텍스트 파일을 구성하는 라인을 하나 하나 필요할때마다 읽어 처리할 수 있는 예입니다.

File file = new File("c:/text.txt");
LineIterator it = null;
try {
    it = FileUtils.lineIterator(file);
    while(it.hasNext()) {
        System.out.println(it.next());
    }
} catch (IOException e) {
    e.printStackTrace();
} finally {
    if(it != null) {
        LineIterator.closeQuietly(it);
    }
}

다음은 파일 경로를 정규화해주는 코드입니다.

 String filename = "c:/commons/io/../../lang/../project.txt";
  String normailzed = FilenameUtils.normalize(filename);
  System.out.println(normailzed);

위 코드의 결과는 c:/project.txt가 됩니다.

그리고 해당 경로에 대한 저장 장치의 가용 용량을 얻는 코드입니다.

try {
    long freeSpace = FileSystemUtils.freeSpaceKb("c:/");
    System.out.println(freeSpace + "kb");
} catch (IOException e) {
    e.printStackTrace();
}

이 외에도 파일의 복사, Endian 처리, 다양한 응용 스트림 클래스 등을 제공합니다. 이 처럼 일반적으로 많이 사용되는 IO 루틴에 대해서 더 짧은 코드를 통해서, 그리고 검증된 방법을 통해 구현할 수 있는 라이브러리를 사용한다면 원하는 기능을 더욱 빠르고 안정적으로 추가 개발할 수 있습니다. 이러한 라이브러리를 미리 알고 있는 것으로 개발자의 역량은 기술면에서 더 높아질 것입니다.