BLOG ARTICLE 안드로이드 | 8 ARTICLE FOUND

  1. 2010.11.12 구글맵 주소검색 연동하기
  2. 2010.10.05 BMP 32/16Bit to 24Bit 변환 (1)
  3. 2010.09.18 Parking MyCar 사용법 (2)

Smart Phone (Mobile)/Android 2010.11.12 13:37

안드로이드에서 구글맵을 연동하여 어플을 개발하다보면 위/경도 좌표에 대한 주소를 알고 싶은 경우가 있다.
물론 안드로이드 기본 SDK에서 주소정보를 제공하나 그렇게 맘에 들지 않아 직접 구글맵의 주소검색 서비스를 연동하는 방식을 알아봤고, 현재 만족하게 사용을 하고 있다.

의외로 참 간단하다. HttpClient를 이용하여 연동하면 끝.

* 구글맵 주소검색 URL
    http://www.google.com/maps/api/geocode/xml?latlng=37.512421,127.058815&sensor=true&language=ko
    - 코엑스의 위치에 대한 한글주소를 XML 형태로 가져올 수 있다.
    - latlng 파라미터는 위/경도 값이며, language는 받아보는 주소에 대한 언어 값이다. (ko=한글, en=영문 등)
    - 물론 XML이 아닌 JSON이나 다른 형태도 제공한다.
    - 참고로 주소에 대한 위치정보(지오코드)를 쉽고 다수로 얻으려면 
      http://www.biz-gis.com/bizBean/Land/Land.html 이곳이 편리하다.


   
* 주소검색 샘플 소스
public String searchAddressXml(double latitude, double longitude) throws Exception {
    String addressXml = null;

    String displayLanguage = context.getResources().getConfiguration().locale.getDisplayLanguage();
    String googleUrl = "http://www.google.com/maps/api/geocode/xml?latlng=%s,%s&sensor=true&language=%s";
    // 단말의 언어가 한국어로 설정되어 있으면 주소를 한글로 설정하고, 이외는 영어로 설정한다.
    String language = "한국어".equals(displayLanguage) ? "ko" : "en";

    googleUrl = String.format(googleUrl, String.valueOf(latitude), String.valueOf(longitude), language);

    HttpClient httpClient = null;
    try {
        httpClient = new HttpClient();
        HttpGet mGetMethod = new HttpGet(uri);
        HttpResponse mHttpRes = httpClient.execute(mGetMethod);

        if (mHttpRes.getStatusLine().getStatusCode() == 200) {
            LineNumberReader reader = null;
            try {
                reader = new LineNumberReader(new InputStreamReader(mHttpRes.getEntity().getContent()));
                StringBuilder httpResStr = new StringBuilder();
                while ((readStr = reader.readLine()) != null) {
                    httpResStr.append(readStr);
                }

                addressXml = httpResStr.toString();
            } finally {
                if (reader != null) {
                    reader.close();
                    reader = null;
                }
            }
        } 
    } finally {
        if (httpClient != null) {
            httpClient.shutdown();
            httpClient = null;
        }
    }

    return addressXml;
}

위 샘플 소스는 위/경도의 값을 받아서 주소를 검색하는 코드이여, 단말의 언어가 한국어로 되어 있으면 주소를 한글로 요청하며, 그외에는 영문으로 요청하게 된다.
코드의 결과는 XML이며, 위의 샘플 소스에  PullParser를 연결하여 분석하면 핸들링이 더욱 쉬워진다.
참 간단하지 않은가요.

저작자 표시 비영리 변경 금지
신고

Smart Phone (Mobile)/Android 2010.10.05 16:39

안드로이드의 Bitmap은 기본적으로 16Bit(RGB_565)를 제공하며, 32Bit(ARGB_8888)을 지원한다.
그러나 이미지 처리를 위해서 Bitmap을 24Bit(RGB_888)로 변경을 해야하는 경우도 있다.
이런 경우는 기본 Android Platform에서는 지원을 하지 않고 있다. 변경이 필요하다면 소스코드에서 실제 데이터 값의 변경 작업으로 수행을 해야 하는 것 같다.

기본적으로 24Bit의 헤더 정보를 생성하야 하며, 16Bit 또는 32Bit Bitmap의 색상데이터 값에서 24Bit로 변경하는 작업을 수행해야 한다.

16Bit Bitmap : #RRRRRGGGGGGBBBBB (RGB_565)
24Bit Bitmap : #RRRRRRRRGGGGGGGGBBBBBBBB (RGB_888)
32Bit Bitmap : #AAAAAAAARRRRRRRRGGGGGGGGBBBBBBBB (ARGB_8888)

16Bit를 24Bit 또는 32Bit로의 변환은 헤더정보가 동일하며, Android에서 pixel 값을 int 형으로 추출하므로 동일하게 처리를 할 수 있다. 물론 int 형이 아닌 실 byte[]의 값으로 가져오는 것이 처리 속도면에서는 좋으나, byte에 대한 데이터형의 차이에 때문에 조금 번거로워 int 형의 픽셀값으로 처리를 한다.

* Bitmap 헤더 정보 생성
private static void encode24BMPHeader(DataOutputStream stream, int width,
        int height, int fileSize) throws IOException {
    // the magic number used to identify the BMP file: 0x42 0x4D
    stream.writeByte(0x42);
    stream.writeByte(0x4D);
    stream.writeInt(swapEndian(fileSize));
    // reserved
    stream.writeInt(0);
    // the offset, i.e. starting address of the bitmap data
    stream.writeInt(swapEndian(14 + 40));
    // INFORMATION HEADER (Windows V3 header) the size of this header (40 bytes)
    stream.writeInt(swapEndian(40));
    // the bitmap width in pixels (signed integer).
    stream.writeInt(swapEndian(width));
    // the bitmap height in pixels (signed integer).
    stream.writeInt(swapEndian(height));
    // the number of colour planes being used. Must be set to 1.
    stream.writeShort(swapEndian((short) 1));
    // the number of bits per pixel, which is the colour depth of the image.
    stream.writeShort(swapEndian((short) 24));
    // the compression method being used.
    stream.writeInt(0);
    // image size. The size of the raw bitmap data. 0 is valid for uncompressed.
    stream.writeInt(0);
    // the horizontal resolution of the image. (pixel per meter, signed integer)
    stream.writeInt(0);
    // the vertical resolution of the image. (pixel per meter, signed integer)
    stream.writeInt(0);
    // the number of colours in the colour palette, or 0 to default to 2n.
    stream.writeInt(0);
    // the number of important colours used, or 0 when every colour is important. generally ignored.
    stream.writeInt(0);
}
실제 Bitmap의 헤더 정보에 대한 부분은 구글을 통해 손쉽게 알 수 있을 것이다.

* Bitmap 픽셀 값의 변경 (32/16Bit to 24Bit)
public static byte[] encodeBMPTo24(int[] rgb, int width, int height) throws IOException {
    int padding = (4 - (width % 4)) % 4;
    // the size of the BMP file in bytes
    int fileSize = 14 + 40 + height * (padding + width * 3);

    ByteArrayOutputStream bytes = new ByteArrayOutputStream(fileSize);
    DataOutputStream out = new DataOutputStream(bytes);

    // encode bitmap header (24bit over)
    encode24BMPHeader(out, width, height, fileSize);

    // PALETTE (none for 24 bit depth) IMAGE DATA starting in the bottom left, working right and then up
    // a series of 3 bytes per pixel in the order B G R.
    int colorValue = 0;
    for (int j = height - 1; j >= 0; j--) {
        for (int i = 0; i < width; i++) {
            colorValue = rgb[i + width * j];
    
            out.writeByte(colorValue & 0x000000FF);
            out.writeByte((colorValue >>> 8) & 0x000000FF);
            out.writeByte((colorValue >>> 16) & 0x000000FF);
        }

        // number of bytes in each row must be padded to multiple of 4
        for (int i = 0; i < padding; i++) {
            out.writeByte(0);
        }
    }

    byte[] encodeBytes = bytes.toByteArray();
    bytes.close();

    // quick consistency check
    if (encodeBytes.length != fileSize) {
        throw new RuntimeException("bitmap file size bad match (32bit to 24bit)");
    }

    return encodeBytes;
}

위의 소스를 이용하여 실제 변경하는 소스는 아래와 같다.

public static byte[] decode24Bitmap(Bitmap bitmap) throws IOException {
    
int width = bitmap.getWidth();
    int height = bitmap.getHeight();
    int[] rgb = new int[width * height];
    bitmap.getPixels(rgb, 0, width, 0, 0, width, height);

    byte[] bitmap24Bytes = encodeBMPTo24(rgb, width, height);
    
    bitmap.recycle();
    bitmap = null;
    rgb = null;
  
    // system gc
    //System.gc();

    return bitmap24Bytes;
}

저작자 표시 비영리 동일 조건 변경 허락
신고


Parking MyCar 안드로이드 어플은 주차정보를 관리해주는 어플이다.
관리 되는 주차정보는 주차 시간, 경과 시간, 출차 시간, 주차 위치, 주차 사진 켭쳐 등이 있다.


* 주요 기능
1. Parking Here! (Camera)
    카메라를 이용하여 사진 켭쳐 후 주차정보 저장 기능. (맵을 이용한 위치정보 포함)
    지하 주차장 등 위치정보 외 주위 이미지 정보가 필요한 경우
2. Parking Here! (Map)
    주위 이미지 정보 없이 Map 상의 위치정보만으로 주차정보 저장 기능.
3. Find MyCar
    저장 된 주차정보를 확인하는 기능.
    주차 정보는 리스트 형식으로 표현되며, 주차정보를 롱클릭하여 [출차], [삭제], [상세보기]의 추가 기능을 사용할 수 있다.

실내(지하) 주차장에서는 주차 블럭의 켭여 된 이미지와 위치 정보를 이용하여 주차 된 나의 차를 쉽게 찾을 수 있으며, 주차 경과 시간을 확인 할 수 있습니다.
GPS의 오차 범위로 인해 주차 된 위치를 사용자가 직접 선택할 수 있어 정확한 위치 정보로 확인이 가능합니다. 



* 화면 구성

Parking MyCar의 화면은 크게 5개의 메인 화면으로 구성되며, 각각의 서브 화면들을 가진다.
1. 메인 화면
    Parking MyCar 어플의 메인 화면으로 메인 기능을 제공하는 화면으로 이동 할 수 있는 화면
2. 카메라 프리뷰 화면
    주차정보로 사용할 사진 이미지를 켭쳐하기 위한 카메라 프리뷰 화면
3. 주차위치 확인 맵 화면
    주차위치를 지정하기 위한 맵화면으로 나의 현재 위치 또는 맵을 클릭하여 직접 위치를 지정할 수 있는 화면
4. 주차 정보 리스트 화면
    저장 된 주차정보의 간략한 정보를 리스트로 보여주는 화면
5. 주차 상세정보 화면
    특정 주차정보에 대해 주차위치를 확인하기 위한 맵화면과 상세한 주차정보 확인 할 수 있는 팝업 화면


* 상세 기능
1. 주차정보 저장히기
    - 주차정보를 저장은 위치정보는 기본이며, 이미지 정보는 옵션이다.
    - 이미지 정보 저장 옵션에 따라 메인에서의 메뉴가 달라지며 화면의 흐름도 달라진다.
      메인화면의 Parking Here! 메뉴가 주차정보 저장메뉴이며, 카메라 아이콘이 이미지 정보가 포함하며, 지도 아이콘은 이미지 정보를 포함하지 않는다.
1.1. 이미지 정보 저장
- 안드로이드 폰의 카메라의 프리뷰 화면이 보이게 되며, 중앙의 흰색박스안에 저장하려는 이미지를 위치시키면 된다.
- 주로 지하주차장의 블럭이미지를 넣으면 유용하다.
- 상단의 돋보기 아이콘은 프리뷰화면의 줌버튼이며, 클릭하면 줌을 설정할 수 있는 팝업뷰가 나타나게 된다.
   (단, 카메라 하드웨어 또는 제조사에 따라 지원하지 않음)
- AF/MF 아이콘은 이미지를 켭쳐할 때 오토포커스의 설정을 변경할 수 있게 된다. (AF : Auto-Focus)
- Capture 버튼은 중앙의 횐색박스 안에 투영되는 영상을 켭쳐하여 이미지로 저장하는 버튼이며, 클릭시 켭쳐된 이미지가 팝업화면으로 보이며 저장과 취소를 선택할 수 있다. 저장을 선택 시 최종 이미지 정보를 저장하며 위치를 선택 할 수 있는 Map 화면으로 넘어간다.
1.2. 위치정보 저장
- 주차 된 위치정보는 GPS와 네트웍에 의해 처리되며, 실내에서는 오차가 발생할 수 있다.
- 위치정보의 선택은 현재 위치와 사용자가 직접 위치를 선택하는 2가지 기능이 제공된다.
- 기본으로 현재 위치가 MAP에 표현되며, 현재 위치는 GPS 또는 Network에 따라 변경 될 수 있으며, 사용자의 움직임에 따라서도 변경 될 수 있다. (위치지정 버튼을 클릭하면 사용자가 위치를 지정할 수 있다.)
- 위치지정 버튼을 클릭 후 사용자 위치지정 모드로 변경되면 사용자가 MAP 상에서 위치를 클릭하여 선택이 가능하다. (내위치 버튼을 클릭하면 현재 위치 지정으로 변경된다.)
- 현재 위치는 사람모양의 마커로 표현되며, 사용자 지정 위치는 자동차모양의 마커로 표현된다.
- 위치저장 버튼을 클릭하여 최종적으로 주차정보를 저장하게 된다. (저장되는 정보로는 이미지(옵션), 위치, 주차시간)
3. 주차정보 리스트 확인
- 저장 된 간략한 주차정보를 리스트 화면으로 볼수 있으며, 저장 된 모든 주차정보가 표현된다.
- 10개의 주차정보씩 표현이 되며, 더 많은 정보는 리스틔 하단에 더보기 버튼을 클릭하여 추가로 로딩되며, 최근 주차정보 순이다.
- 리스트에 이미지 썸네일, 주차시간, 상태, 주소정보, 위치정보, 경과된 주차시간을 확인 할 수 있다.
- 주소정보는 저장 시 네트웍의 상태에 따라 표현되지 않을 수 있다.
- 특정 주차정보를 롱클릭하게 되면 서브메뉴 팝업이 나타난다.
- 출차처리 : 선택 된 주차정보가 출차되었음을 처리한다.
- 삭제 : 선택 된 주차정보를 삭제한다.
- 상세보기 : 선택 된 주차정보의 상세뷰(MAP)을 보여준다.
- 주차경과 시간은 1시간까지는 검은색, 2시간까지는 파란색, 3시간이상은 빨간색으로 표현된다.
- 주차 이미지가 저장되어 있으면 썸네일로 보여지며, 없다면 기본 맵이미지로 표현된다.
4. 주차정보 상세보기 (MAP)
- 주차정보의 상세뷰는 MAP과 상세정보 팝업 2부분으로 나눠져있다.
- MAP 화면에는 나의 현재위치와 주차 된 차의 위치가 표현된다.
- 사람 버튼을 클릭 시 MAP이 나의 위치가 중앙으로 오게 이동된다.
- 자동차 버튼을 클릭 시 MAP이 주차 된 자동차의 위치가 중앙으로 오게 이동된다.
- 주차 정보 보기 버튼을 클릭 시 주차 상세보기 팝업을 볼수 있다.
- 주차 상세보기에는 이미지정보, 주차시간, 상태, 주소정보, 위치정보, 경과시간, 출차시간, 나와 주차 된 차의 거리(직선거리)를 확인 할 수 있다.
- 경과 시간은 주차정보 리스트와 동일한 색깔로 표현이 된다.
- 사용자가 이동하게 되면 MAP의 나의 마커가 이동하게 되며, 주차 되 차로 쉽게 이동할 수 있으며, 주차거리도 변경된다.

* 오류 및 문의
Parking MyCar 에 대한 오류 내용이나 문의는 아래의 트위터나 이메일을 이용하시면 됩니다.
    트위터 : @ukzzang
    이메일 : ukzzang@gmail.com

향후 좀더 유용한 기능의 추가와 업데이트를 통해 계속해서 찾아뷥겠습니다. 감사합니다.

저작자 표시 비영리 변경 금지
신고