Firestore, GoogleMap: DB에서 읽어서 구글맵에 마커 만들기

3 분 소요


</br> 프로젝트에서 만든 함수다. DB에 저장된 고양이 정보를 읽어 구글맵에 보여 준다.
</br> 1 저장된 DB 형태는 이런 식이다.
catMarkers 컬렉션에 각 고양이 별로 문서가 있다. 문서의 필드에 위도, 경도, 이름, 타입이 있다.
2
위도와 경도로 구글맵 마커를 위치시키고, 각 마커마다 이름을 title에 달아 주며, 타입에 따라 마커의 이미지가 달라진다.

/*
  DB에서 정보 들고 와서 마커 보여주기
*/
public void setMarkersFromDB() {
    Log.d("Marker", "set marker");
    mDatabase.collection("catMarkers")
    .get()
    .addOnCompleteListener(task -> {
        if( task.isSuccessful() ){
            Log.d("Marker", "Successful");
            String catName = "?";
            String type = "?";
            double latitude = 0.0;
            double longitude = 0.0;
            for(QueryDocumentSnapshot document : task.getResult()){
                Map<String, Object> getDB = document.getData();
                Object ob;
                if( (ob = getDB.get("name")) != null ){
                    catName = ob.toString();
                    if( !(catNames.contains(catName)) ){
                        catNames.add(catName);
                    }
                }
                if( (ob = getDB.get("type")) != null ){
                    type = ob.toString();
                    namesAndTypes.put(catName, type);
                }
                if( (ob = getDB.get("latitude")) != null ){
                    latitude = Double.parseDouble(ob.toString());
                }
                if( (ob = getDB.get("longitude")) != null ){
                    longitude = Double.parseDouble(ob.toString());
                }
                Log.d("Marker Info", catName + " " + type);
                MarkerOptions markerOptions = new MarkerOptions();
                markerOptions.position(new LatLng(latitude, longitude))
                .title(catName)
                .snippet("반가워요")
                .icon(BitmapDescriptorFactory.fromResource(getResources().getIdentifier(type,"drawable",getPackageName())));
                mMap.addMarker(markerOptions);
            }
        }
        else{
            Log.d("Marker", "Error show DB", task.getException());
        }
    });
} // End setMarkersFromDB();

전체 함수 코드다.

</br>

DB 읽기

우선

FirebaseFirestore mDatabase  = FirebaseFirestore.getInstance();

이 코드로 mDatabase를 미리 초기화 해 줘야 한다.

// DB에서 컬렉션 읽기
mDatabase.collection(collectionPath)
        .get()
        .addOnCompleteListener(task -> {
          if( task.isSuccessful() ){
            for(QueryDocumentSnapshot document : task.getResult()){
              Map<String, Object> getDB = document.getData();
              if( getDB == null ){
                // do something
              }
            }
          }
          else{
            Log.d("Error", "Error show DB", task.getException());
          }
        });

구글맵 부분은 빼고 기본만 들고 왔다.
mDatabase.collection(collectionPath)로 컬렉션에 접근할 수 있다.
.get()으로 데이터를 읽고, .addOnCompleteListener()에서 데이터가 다 로드되면 할 일을 하면 된다.
만약 성공적으로 읽었다면, for(QueryDocumentSnapshot document : task.getResult())으로 컬렉션 안의 모든 문서들을 document로 받아 for문을 돌려 다룰 수 있다. 이 때 읽어 온 데이터는 Map<String, Object> 형식이다.

// DB에서 문서 읽기
mDatabase.document(documentPath))
        .get()
        .addOnCompleteListener(task -> {
          if( task.isSuccessful() ){
            Map<String, Object> getDB = task.getResult().getData();
            if( getDB == null ){
              // do something
            }
          }
          else{
            Log.d("Error", "Error show DB", task.getException());
          }
      });

비슷하게 DB에서 문서를 읽을 때다.
문서의 필드에 데이터가 있으므로, 컬렉션을 읽을 경우 컬렉션 안의 모든 문서들의 필드를 조회하기 위해 for(QueryDocumentSnapshot document : task.getResult())으로 문서들을 하나 하나 확인하지만, 그냥 문서를 읽을 때는 바로 task.getResult().getData()로 DB를 읽을 수 있다.
</br>

구글맵에 마커 만들기

for(QueryDocumentSnapshot document : task.getResult()) {
    Map<String, Object> getDB = document.getData();
    Object ob;
    if( (ob = getDB.get("name")) != null ){
        catName = ob.toString();
        if( !(catNames.contains(catName)) ){
            catNames.add(catName);
        }
    }
    if( (ob = getDB.get("type")) != null ){
        type = ob.toString();
        namesAndTypes.put(catName, type);
    }
    if( (ob = getDB.get("latitude")) != null ){
        latitude = Double.parseDouble(ob.toString());
    }
    if( (ob = getDB.get("longitude")) != null ){
        longitude = Double.parseDouble(ob.toString());
    }
    // null 체크를 해 주면 더 안전하다. 안 했는데 혹시 값이 없으면 앱 멈출 수 있음...
    
    MarkerOptions markerOptions = new MarkerOptions();
    markerOptions.position(new LatLng(latitude, longitude))
                .title(catName)
                .snippet("반가워요")
                .icon(BitmapDescriptorFactory.fromResource(getResources().getIdentifier(type,"drawable",getPackageName())));
    mMap.addMarker(markerOptions);
}

마커 만드는 부분은 아래 7줄이다.
MarkerOptions markerOptions = new MarkerOptions();로 마커를 만들기 위한 정보들을 저장할 마커 옵션 객체를 만든다. .position(new LatLng(latitude, longitude))으로 위도와 경도로 마커를 만들 수 있다. 이까지만 해도 기본적인 마커는 만들어진다.
.title()은 타이틀, 위 사진에서 봤듯이 위의 굵은 글씨고 .snippet()은 아래의 작은 글씨다.
.icon()으로 아이콘을 설정할 수 있다.
BitmapDescriptorFactory.fromResource(getResources().getIdentifier(type,"drawable",getPackageName()))를 나눠서 보면
  getResources().getIdentifier(type,"drawable",getPackageName())type이라는 이름의 파일을 리소스에서 찾아서 얻고,
  BitmapDescriptorFactory.fromResource()로 해당 리소스로 비트맵 디스크립터를 만들어 넘겨준다.
마지막으로 mMap.addMarker(markerOptions);로 맵에 마커를 추가할 수 있다.
</br>

주의할 점

public void someFunction() {
    int a = 0, b = 0;
    mDatabase.collection(collectionPath)
        .get()
        .addOnCompleteListener(task -> {
          if( task.isSuccessful() ){
            for(QueryDocumentSnapshot document : task.getResult()){
              Map<String, Object> getDB = document.getData();
              if( getDB == null ){

                // *********** 2
                read and set a = 10, b = 10;
                // ***********

                // *********** 3
                print a + b
                // ***********

              }
            }
          }
          else{
            Log.d("Error", "Error show DB", task.getException());
          }
        });

    // *********** 1
    print a + b
    // ***********

}

혹시 실수할 수 있는 부분인데~ 위에서 출력 결과는 0, 20이다.
3
DB에서 값을 읽는 데에 시간이 걸리기 때문이다. 비동기식으로 DB에서 읽어 올 동안 다른 일을 할 수 있게 하기 때문에, 표시한 바와 같이 가장 아래의 프린트문이 먼저 실행되고 그 다음 DB에서 값을 읽고 설정하고 프린트하게 될 것이다.
따라서 .addOnCompleteListener()에서 읽기가 complete 됐을 때에 처리를 해야 한다~~

댓글남기기