안녕하세요! miTHON의 위치 정보 및 안드로이드 서비스 단 담당 강현지입니다.


구글 지도 API 활용하기 두번째 포스팅으로 찾아왔습니다!!!


이번 포스팅은 지난번 포스팅에 말씀드린대로 현재 위치 받아오기실시간 트래킹에 대해 살펴보려고 합니다.



[ 4단계: 현재 위치 받아오기 ]


현재 위치를 실시간으로 받아와서 지도에 마커를 표시하는 코드를 아래와 같이 작성하였습니다. 


/**
* 현재 위치정보 조회.
*
* @return
*/
public Maybe<Location> get_현재위치정보() {
try {
// 권한 체크.
CheckUtil.Check(ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_FINE_LOCATION)
== PackageManager.PERMISSION_GRANTED, "[ACCESS_FINE_LOCATION] 권한체크 에러", Exception.class);
CheckUtil.Check(ContextCompat.checkSelfPermission(mContext, Manifest.permission.ACCESS_COARSE_LOCATION)
== PackageManager.PERMISSION_GRANTED, "[ACCESS_COARSE_LOCATION] 권한체크 에러", Exception.class);

// 위치정보 조회.
LocationManager locationManager = (LocationManager) mContext.getSystemService(Context.LOCATION_SERVICE);

// 위치 정보 사용여부 조회.
{
CheckUtil.Check(get_위치정보_요청가능_여부(locationManager), "위치정보 사용불가능 상태", Exception.class);
}

// 네트워크 정보로부터 데이터 조회.
{
// 네트워크 정보로 부터 위치값 가져오기
if (isNetworkEnabled) {
locationManager.requestLocationUpdates(
LocationManager.NETWORK_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES
, this
);

if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
}
}
}

// GPS 정보로부터 데이터 조회.
{
if (isGPSEnabled) {
if (location == null) {
locationManager.requestLocationUpdates(
LocationManager.GPS_PROVIDER,
MIN_TIME_BW_UPDATES,
MIN_DISTANCE_CHANGE_FOR_UPDATES,
this
);

if (locationManager != null) {
location = locationManager.getLastKnownLocation(LocationManager.GPS_PROVIDER);
}
}
}
}

} catch (Exception e) {
Log.e(MiRunConstDefinition.LOG_NAME, "위치정보 획득 실패", e);
}

return MaybeUtil.JustNullable(location);
}
/**
* 마커 그리기
*
* @param myLocationMaybe
*/
public void setDrawMaker(Maybe<Location> myLocationMaybe) {

MaybeUtil.Subscribe(myLocationMaybe,

location -> {

// Creating a LatLng object for the current location
LatLng latLng = new LatLng(location.getLatitude(), location.getLongitude());

CameraPosition cp = new CameraPosition.Builder().target((latLng)).
zoom(17).
build();

// Showing the current location in Google Map
mGoogleMap.moveCamera(CameraUpdateFactory.newCameraPosition(cp));

// 기존 마커 삭제.
if (marker != null) {
marker.remove();
}

// 마커 설정.
MarkerOptions optFirst = new MarkerOptions().
position(latLng).
icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_marker_pink)).
title(MiRunResourceUtil.GetString(R.string.label_record_current_position));

marker = mGoogleMap.addMarker(optFirst);
},
() -> MiRunViewUtil.ShowToast(R.string.label_emergency_request_location_fail));
}






[ 5단계: 실시간 트래킹 ]


실시간 위치정보를 받아 트래킹 라인을 그리는 코드는 아래와 같습니다.


/**
* 지역 정보 목록 세팅.
*
* @param locationList
* @param myLocationMaybe
*/
public void setLocationList(@NonNull List<LocationConcreteVO> locationList, Maybe<Location> myLocationMaybe) {
// TODO khj1219

// 위도경도 목록 제작.
List<LatLng> latLngList = new ArrayList<>();
{

Observable.fromIterable(locationList).
forEach(vo -> {
// 위도 경도 처리.
latLngList.add(new LatLng(vo.getLatitude(), vo.getLongitude()));
Log.d(MiRunConstDefinition.LOG_NAME, String.format("초기화용 데이터 -> 위도 : %f, 경도 : %f, 시간 : %d",
vo.getLatitude(), vo.getLongitude(), vo.getTime()));
});

myLocationMaybe.
map(location -> new LatLng(location.getLatitude(), location.getLongitude())).
subscribe(latLngList::add);
}

// 마지막 라인 마커찍기.
{
if (!ContainerUtil.IsEmpty(latLngList)) {
setDrawMaker(myLocationMaybe);
}
}

AsyncBuilder.Create((FlowableEmitter<Double> emitter) -> {
if(latLngList.size() > 1){
Double result = getRecordDistance(latLngList);
emitter.onNext(result);
emitter.onComplete();
}
}).doOnNext(result -> {
// 거리 업데이트.
if(!Double.isNaN(result)){
setTextByDistance(result);
setTextByDonation(result * MiRunConstDefinition.CONST_DONATION.RUN_PER_REWARD);
}
}).run();

// 달리는 경로 polyline 생성.
drawRunningPolyline(latLngList);
}
/**
* 달리는 경로 polyline 생성(pink)
*
* @param runningPoints
*/
public void drawRunningPolyline(List<LatLng> runningPoints){
PolylineOptions polylineOptions = new PolylineOptions();
polylineOptions.color(MiRunResourceUtil.GetColor(R.color.color_path_pink));
polylineOptions.width(7);
polylineOptions.addAll(runningPoints);
mGoogleMap.addPolyline(polylineOptions);
}




이것으로 구글 지도 API 활용하기 5단계에 대해 모두 살펴보았습니다.


트래킹의 경우 테스트가 매우 힘들었지만 결과적으로 앱이 잘 나와서 매우 행복합니다.


드디어 발표가 내일인데 좋은 결과 있었으면 좋겠네요.


아직까지 열심히 디비 작업중이신 우리 유감독님 더불어 피티엥 혼신의 힘을 다하고 계신 남배우님 정말 존경하고 조금만 힘내세요!!!


우리 모두 화이팅 입니다~~


반응형
Posted by N'

안녕하세요! miTHON의 위치 정보 및 안드로이드 서비스 단 담당 강현지입니다.


MIDAS Research Festival의 해커톤인 ESC 주제로 저희는 마라톤 앱인 miTHON에 지도 관련 기능이 많습니다.


아래 기획서를 받고 구현을 위해 구글 지도 API를 활용하였고, 그 내용을 공유하고자 게시글을 올리게 되었습니다~


그럼 우선 기획서부터 살펴볼까요?



1) [그룹] 이벤트 생성하기 (경로 생성, 식수대 처리)





2) [개인][메인] 기록 시작




위 기획서를 바탕으로 구글 지도 API를 활용하였고, 그 기능을 5단계로 설명하려고 합니다.


해당 게시글에서는 3단계까지 소개하겠습니다.


[ 1단계: 맵뷰 띄우기 ]


1. 사이트에 가서 API Key를 발급 받습니다.

(레퍼런스: https://developers.google.com/maps/documentation/android-api/signup?hl=ko)

 


2. app 폴더 밑에 있는 build.gradle에 가서 구글 맵 API 컴파일 설정을 해줍니다. Google Play Services 전체 라이브러리(com.google.android.gms:play-services)를 추가하는 대신에 Google Maps Android API를 사용하기 위해 필요한 라이브러리만 추가해주는 것이 좋습니다.

그리고 SDK Manger에서 Google Play services를 설치한다. (설치 안하고 빌드시 에러와 동시에 설치하는 링크를 제공할 것입니다.)

// Google Maps Android API
compile 'com.google.android.gms:play-services-maps:11.0.2'
compile 'com.google.android.gms:play-services-location:11.0.2'

구글 플레이 서비스를 이용하려는 해당 뷰에 아래 코드를 넣으면 구글 플레이 서비스를 이용할 수 있습니다.

GoogleApiAvailability.getInstance().isGooglePlayServicesAvailable(this);

 

3. AndoridManifest.xml에 가서 아래와 같이 meta-data 설정을 해줍니다.

<!-- 구글 API KEY 설정 -->
<meta-data
   
android:name="com.google.android.geo.API_KEY"
   
android:value=" 발급받은 API key'" />

 


4. 맵뷰를 구현할 화면의 onCreate 메소드에 SupportMapFragment를 이용해 지도 객체를 생성합니다. Fragment 객체를 지도를 처리할 Activity)에 추가한다. 프래그먼트에서 getMapAsync()를 호출하여 콜백을 등록합니다.

(레퍼런스: https://developers.google.com/maps/documentation/android-api/map?hl=ko)

@Override
protected void onCreate(Bundle savedInstanceState) {
   
super.onCreate(savedInstanceState);

   
// BitmapDescriptorFactory 생성하기 위한 소스
   
MapsInitializer.initialize(getApplicationContext());

   
SupportMapFragment mapFragment = ((SupportMapFragment) getSupportFragmentManager().findFragmentById(R.id.map));
   
mapFragment.getMapAsync(this);
}

 

5. 맵뷰를 구현할 화면에 OnMapReadyCallback 인터페이스를 구현하여 onMapReady 메소드를 오바라이딩 합니다. onMapReady은 콜백 메소드로서 GoogleMap 객체의 핸들을 가져옵니다.

(레퍼런스: https://developers.google.com/android/reference/com/google/android/gms/maps/OnMapReadyCallback )

public interface OnMapReadyCallback: Callback interface for when the map is ready to be used.

@Override
public void onMapReady(final GoogleMap googleMap) {

   
this.mGoogleMap = googleMap;

   
String coordinates[] = {"37.397446", "127.105714"};
    double
lat = Double.parseDouble(coordinates[0]);
    double
lng = Double.parseDouble(coordinates[1]);

   
LatLng position = new LatLng(lat, lng);
   
GooglePlayServicesUtil.isGooglePlayServicesAvailable(MapActivity.this);

   
// 맵 위치이동.
   
this.mGoogleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(position, 15));

}

 

위의 코드 실행결과 아래와 같이 지정한 위경도 좌표에 포커스가 된 맵뷰를 나타낼 수 있습니다.






[ 2단계: 화면에 마커 표시 및 마커 클릭 이벤트 ]


1. 맵뷰가 구현된 화면에 OnMapClickListener, OnMapLongClickListener, OnInfoWindowClickListener 인터페이스를 구현하여 OnMapClick, OnMapLongClick, OnInfoWindowClick 메소드를 오버라이딩합니다.

(레퍼런스: https://developers.google.com/android/reference/com/google/android/gms/maps/GoogleMap.OnMapClickListener)

 

2. 1단계에서 오버라이드한 OnMapReady 메소드의 구글 맵 지도에 클릭 이벤트를 세팅합니다.

@Override
public void onMapReady(final GoogleMap googleMap) {
    mGoogleMap = googleMap
;

    
mGoogleMap.setOnMapClickListener(this);
   
mGoogleMap.setOnMapLongClickListener(this);
   
mGoogleMap.setOnInfoWindowClickListener(this); //정보창 클릭 리스너(마커 삭제 이벤트)
}

 

3. 오버라이딩한 OnMapClick, OnMapLongClick, OnInfoWindowClick 메소드에 아래와 같이 클릭했을 때 나타날 이벤트를 설정합니다. 이때 MarkerOptions를 이용해 마커를 커스터마이징 할 수 있습니다. (마커가 표시될 위치, 아이콘, 타이틀 등 추가 가능)

 

- 지도를 클릭했을 때 "출발" 마커 생성

@Override
public void onMapClick(LatLng latLng) {
    MarkerOptions markerOptions =
new MarkerOptions();

       
//add marker
       
markerOptions.position(latLng);
       
markerOptions.icon(BitmapDescriptorFactory.fromResource(R.mipmap.ic_marker_pink));
       
markerOptions.title("출발");
       
mGoogleMap.addMarker(markerOptions).showInfoWindow();

       
// 맵셋팅
       
arrayPoints.add(latLng);
       
arrayMarkerOptions.add(markerOptions);
}

 

- 지도를 길게 클릭했을 때 polyLine 생성

@Override
public void onMapLongClick(LatLng latLng) {
   
// TODO
    //
마커를 길게 누를 경우 발생할 이벤트 작성
}

 

- 마커 info창을 클릭했을 때 해당 마커 삭제 이벤트

@Override
public void onInfoWindowClick(Marker marker) {
   
// step2에서만 삭제 가능 && 시작 위치는 삭제 못함
   
if((getViewPager().getCurrentItem() == 1) && !arrayPoints.get(0).equals(marker.getPosition())){
       
// 지도 초기화
       
mGoogleMap.clear();

       
// 리스트에서 마커 삭제
       
arrayPoints.remove(marker.getPosition());
       
arrayMarkerOptions.removeIf(v -> v.getPosition().equals(marker.getPosition()));
   
}
}


        




[ 3단계: 마커를 연결하는 Polyline 생성 ]


1. 마커를 연결할 polyLine 생성은 PolylineOptions를 사용합니다. (라인 색, 두께 등 지정 가능)

// polyline 생성
public void drawPolyline(){
    polylineOptions =
new PolylineOptions();
   
polylineOptions.color(Color.RED);
   
polylineOptions.width(5);
   
polylineOptions.addAll(arrayPoints);
   
mGoogleMap.addPolyline(polylineOptions);
}

 

2. [2단계] OnMapLongClick 메소드에 Polyline을 그리는 로직을 작성하여 마커를 길게 누를 경우 마커 리스트를 연결하는 polyLine을 만들 수 있습니다.

@Override
public void onMapLongClick(LatLng latLng) {
   
if(getViewPager().getCurrentItem() == 1) { // 2page
       
polylineOptions.addAll(arrayPoints);
       
mGoogleMap.addPolyline(polylineOptions);
   
}
}




우선 여기까지 해서 구글 지도 API 활용하기 포스팅을 마치겠습니다.


구글 지도 API는 처음 사용하시는 분들도 쉽게 사용할 수 있을 만큼 API 문서도 잘 정리되어있고, 예제 소스도 많이 있습니다.


생각보다 지도 관련 결과물이 잘 나와서 매우 뿌듯하네요~ 다음번 포스팅은 현재 위치 받아오기실시간 트래킹에 대해 소개해 드리겠습니다.


그럼 다음 포스팅에서 봐요~~


4-GRAM 화이팅입니다!!! 우리 조원 너무 좋아 ^______^!!


반응형
Posted by N'

안녕하세요 :) 저희 앱(miTHON)의 서버운영/DB/백엔드 담당 유덕형입니다.


MIDAS Research Festival 의 강력한 우승후보답게 저희는 상당한 수준의 개발 완성도를 자랑하는데요.


제가 담당한 부분을 이제부터 차근차근 설명을 드리는 시간을 가져보겠습니다.


먼저 저희 서버 아키텍처에 대해 설명을 드려볼까 합니다.


저희 서버는 AWS (Amazon Web Service) 에 올라가있습니다.

AWS 의 장점은 이루 말할 수 없을 정도로 많은데요. (자세한 내용은 저보다 구글을 참고해주세요.)


1. AWS 간략 설계 아키텍처



AWS 에는 각각의 기능에 최적화된 서버를 구성할 수 있도록 다양한 모듈들을 지원하는데요.

저희는 DB를 MySQL 을 사용하므로 Relation Database 를 구축할 수 있는 RDS 를 선택했습니다.

또한 파일 업/다운로드에 용이하며 정적 서버 호스팅도 지원하는 S3 를 사용했습니다. 

S3 의 장점은 바로 다운로드 URL 을 만들어 제공한다는 것인데요. 저희는 파일서버에 구성원 사진과 SNS 사진들을 저장하고 있습니다.


마지막으로 WAS 를 띄워놓는 EC2 인데요. 역시 서버는 우분투가 진리죠.


2. 백엔드 모듈 아키텍처



서버 백엔드 개발은 웹솔루션에서 사용하고 있는 Spring framework 기반 위에서 진행되었습니다.

보시는건 저희 Class Diagram 입니다. 생각보다 클래스가 많네요? 허허 이것이 저의 클라쓰일까요..

이 수많은 클래스들에 제 영혼이 4Gram 정도 들어간 것 같네요. 노동 착취의 향기가 느껴지지 않나요?


3. DB E-R Diagram


1) 논리모델




2) 물리모델


이번 긴 추서 연휴를 이용하여 저희팀의 DBA 이신 남궁용락 차장님에게 전수받은 기술로...! 

DB 설계를 진행해봤습니다. 저희 앱의 큰 틀이 되는 그룹, 그 하위의 이벤트 등 기획자님이 기획서를 잘 써주신 덕에

편안하게 설계를 진행할 수 있었습니다.

여기서 주목할 점은 member 정보를 독립적으로 가져감에 따라 다른 모듈과의 호환성을 조금 더 편리하게 만들었다는 것입니다.


4. 그리고 개발



착즙 세팅이 완료되었다면 그 기반 위에서 이제 저를 갈아넣기만 하면 됩니다.

코드가 알록달록한게 예쁘네요.


5. 마치며...


기술적인 내용은 전혀 없네요. 결과물 위주의 블로그를 써버림.


총 개발기간은 약 1개월 정도 되었네요. 추석기간동안 좀 더 열심히 할걸... 

ESC (Eat, Sleep, Coding) 의 취지에 맞게 1개월동안

퇴근하면 자기 전까지 밥먹고 개발하고 자고 의 for loop 속에서 살았습니다 ㅋㅋㅋㅋ


그럼에도 항상 웃음을 잃지 않고 정말 열정적으로 착즙한 강현지 사원과 남두현 사원님,

그리고 우리 앱의 기획과 디자인을 담당하신 성지윤 사원님! (군대간 유형주 사원도...)

감사하고 즐거운 시간이었다고 말씀드리고 싶습니다.


2017년이 마무리 되어가는 이 시점에, 그래도 한달 정도는 최고로 열심히 살았구나 하는

춥지만 뜨거운 시간이었습니다.


그럼 우리 마지막까지 힘내서 달려보자구요 !

반응형
Posted by N'