반응형

 

Places API Web Service를 이용하여 현재 위치 주변의 음식점 정보를 안드로이드의 구글맵에 표시하는 내용을 다룹니다.



Google Maps Android API를 사용하는 방법과  FusedLocationProviderClient 를 이용하여 현재 위치를 구글맵에 표시하는 내용을 다음 두 포스팅에서 다루었습니다.

중복된 내용은 빠져있기 때문에 본 포스팅에 있는 내용을 진행하기 전에 미리 검토해보세요..

 

Google Maps Android API 사용 방법 및 예제

http://webnautes.tistory.com/647 

 

Android Google Map에 현재 위치 표시하기( FusedLocationProviderClient 사용)

https://webnautes.tistory.com/1249 




 2016.12. 4

 2017.11.27  주변 장소의 주소가 한글로 나오도록 수정

 2018. 6. 13 

 2019. 7. 19  FusedLocationProviderClient 사용하도록 수정

 2019. 8. 15  androidx,SupportMapFragment 사용하도록 수정

 2020. 6. 21 문제 발생시 추가

 2020. 9. 13 테스트 및 수정 (  Android Studio 4.0.1, Android 11.0  API 30 )

 2021. 5. 11 테스트 및 수정 (  Android Studio 4.1.3, Android 11.0  API 30 )

2022. 6. 18 라이브러리 인식 안되던 문제 해결





1. Places API Web Service 키 얻기

2. Google Map에 현재 위치 주변의 음식점 표시하기

3. 실행 결과

4. 구현시 참고




1. Places API Web Service 키 얻기

현재 위치 주변에 있는 장소를 종류별로 검색하는 기능을 Google Places API for Android에서는 아직 제공하지 않기 때문에 Places API Web Service를 사용합니다.



1. 1. Google Developers Console 사이트 (https://console.developers.google.com/apis/dashboard )에 접속합니다. 



앞에서 Google Map을 위해 생성했던 프로젝트를 선택한 상태에서 진행해야 합니다. 




2. API에 마우스 커서를 가져가면 보이는 메뉴에서 라이브러리를 선택합니다. 

 




3. place api를 검색하여 Places API를 선택합니다.

 



4. 사용을 클릭합니다.

 



5. 왼쪽에 마우스 커서를 가져가면 보이는 메뉴에서 사용자 인증 정보를 클릭합니다.

 



6. 사용자 인증 정보 만들기를 클릭하고 메뉴에서 API 키를 선택합니다. 

 



7. 생성된 API 키를 복사해두고  닫기를 클릭합니다. 

 



2. Google Map에 현재 위치 주변의 음식점 표시하기

Places API Web Service에서 제공하는 REST API를 사용하여 현재 위치 주변의 음식점에 대한 정보를 요청하여 획득된 JSON을 파싱하는 작업을 해주면  되지만 여기에서는 간단하게 구현이 가능한 Android-Google-Places-API 라이브러리(https://github.com/nomanr/Android-Google-Places-API)를 사용했습니다.



다음 포스팅에 있는 코드를 기반으로 수정하는 방법을 설명합니다.

 

Android Google Map에 현재 위치 표시하기( FusedLocationProviderClient 사용)

https://webnautes.tistory.com/1249 



settings.gradle에 다음 코드를 추가합니다. 

t_Haru 님이 댓글로 알려주셔서 반영했습니다. 

 

 

pluginManagement {
    repositories {
        gradlePluginPortal()
        google()
        mavenCentral()
        maven {
            url 'https://maven.google.com'
        }
        jcenter()
        maven { url 'https://maven.fabric.io/public' }
    }
}
dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
        maven {
            url 'https://maven.google.com'
        }
        jcenter()
        maven { url 'https://maven.fabric.io/public' }
    }
}
rootProject.name = "Google Maps Android API Example"
include ':app'




레이아웃 파일( activity_main.xml )에 버튼을 추가하고 android:layout_weight 속성으로 화면에 보이는 지도와 버튼의 높이를 조정합니다. 

 

<?xml version="1.0" encoding="utf-8"?>

    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:id="@+id/layout_main"
    android:orientation="vertical"
    tools:context=".MainActivity" >

    <fragment
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.8"
        android:id="@+id/map"
        tools:context=".MapsActivity"
        android:name="com.google.android.gms.maps.SupportMapFragment" />

    <Button
        android:text="장소검색"
        android:layout_width="match_parent"
        android:layout_height="0dp"
        android:layout_weight="0.2"
        android:id="@+id/button"/>

</LinearLayout>



app 모듈을 위한 build.gradle 파일에서  minSdkVersion15 이상으로 사용해야 합니다. 

 



Android-Google-Places-API 라이브러리를 추가해줍니다.  글 작성시점에서  최신 버전은 1.1.3입니다.

변경된 사항을 반영해주기 위해 상단 우측에 보이는  Sync Now를 클릭합니다.

 

implementation 'noman.placesapi:placesAPI:1.1.3'

 



메니패스트 파일(AndroidManifest.xml)에서 <application> 태그의 allowBackup 속성을 false로 수정합니다.

 

 android:allowBackup="false"

 



MainActivity에서 PlacesListener 인터페이스를 구현해줘야 합니다.

PlacesListener를 추가해줍니다.

 

public class MainActivity extends AppCompatActivity
        implements
        OnMapReadyCallback,
        ActivityCompat.OnRequestPermissionsResultCallback,
        PlacesListener{




다음 다섯 줄을 추가해줍니다.

 

import noman.googleplaces.NRPlaces;
import noman.googleplaces.Place;
import noman.googleplaces.PlaceType;
import noman.googleplaces.PlacesException;
import noman.googleplaces.PlacesListener;



추가한 인터페이스에서 요구하는 다음 4개의 메소드를 추가해줍니다.

 

@Override
public void onPlacesFailure(PlacesException e) {

}

@Override
public void onPlacesStart() {

}

@Override
public void onPlacesSuccess(List<Place> places) {

}

@Override
public void onPlacesFinished() {

}



필요한 변수를 추가해줍니다.

 

List<Marker> previous_marker = null;

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




onPlacesSuccess 메소드의 파라미터 타입에 final을 추가해주고 다음 코드를 입력합니다.

 

@Override
public void onPlacesSuccess(final List<Place> places) {

      runOnUiThread(new Runnable() {
            @Override
            public void run() {
                for (noman.googleplaces.Place place : places) {

                    LatLng latLng
                            = new LatLng(place.getLatitude()
                            , place.getLongitude());

                    String markerSnippet = getCurrentAddress(latLng);

                    MarkerOptions markerOptions = new MarkerOptions();
                    markerOptions.position(latLng);
                    markerOptions.title(place.getName());
                    markerOptions.snippet(markerSnippet);
                    Marker item = mMap.addMarker(markerOptions);
                    previous_marker.add(item);

                }

                //중복 마커 제거
                HashSet<Marker> hashSet = new HashSet<Marker>();
                hashSet.addAll(previous_marker);
                previous_marker.clear();
                previous_marker.addAll(hashSet);

            }
        });

}



showPlaceInformation() 메소드를 추가해줍니다. 

 "Places API Web Service 키" 부분에 따로 복사해둔 키를 입력하셔야 합니다.

 

public void showPlaceInformation(LatLng location)
{
mMap.clear();//지도 클리어

if (previous_marker != null)
previous_marker.clear();//지역정보 마커 클리어

new NRPlaces.Builder()
.listener(MainActivity.this)
.key("Places API Web Service 키")
.latlng(location.latitude, location.longitude)//현재 위치
.radius(500) //500 미터 내에서 검색
.type(PlaceType.RESTAURANT) //음식점
.build()
.execute();
}




onCreate 메소드에  ArrayList 초기화와 버튼 클릭시 showPlaceInformation() 메소드를 호출하는 코드를 추가합니다.

 

setContentView(R.layout.activity_main);


previous_marker = new ArrayList<Marker>();

Button button = (Button)findViewById(R.id.button);
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
showPlaceInformation(currentPosition);
}
});




3. 실행 결과

실행하면 현재 위치가 지도에 표시됩니다.

 

주변 영역이 많이 보이도록 지도의 줌을 적당하게 조정한 후,  장소 검색 버튼을 누르면 현재 위치 주변의 음식점 위치들이 지도에 표시됩니다.

 

결과를 가져오는데 좀 시간이 걸립니다. 

 




음식점 위치를 표시하는 마커 하나를 선택하면 음식점 이름과 주소가 나타납니다.

장소 검색버튼을 다시 누르면 기존 마커들을 다지우고 새로 음식점 마커들을 표시합니다.

 



4. 구현시 참고

https://developers.google.com/places/web-service/supported_types?hl=ko 에 있는 Places API Web Service에서 지원되는 장소 유형을  Android-Google-Places-API 라이브러리에서 다음처럼 정의해놓고 있습니다. 

package noman.googleplaces;

/**
* Created by Noman on 8/25/2016.
* https://developers.google.com/places/supported_types
*/
public interface PlaceType {
    public String ACCOUNTING = "accounting";
    public String AIRPORT = "airport";
    public String AMUSEMENT_PARK = "amusement_park";
    public String AQUARIUM = "aquarium";
    public String ART_GALLERY = "art_gallery";
    public String ATM = "atm";
    public String BAKERY = "bakery";
    public String BANK = "bank";
    public String BAR = "bar";
    public String BEAUTY_SALON = "beauty_salon";
    public String BICYCLE_STORE = "bicycle_store";
    public String BOOK_STORE = "book_store";
    public String BOWLING_ALLEY = "bowling_alley";
    public String BUS_STATION = "bus_station";
    public String CAFE = "cafe";
    public String CAMPGROUND = "campground";
    public String CAR_DEALER = "car_dealer";
    public String CAR_RENTAL = "car_rental";
    public String CAR_REPAIR = "car_repair";
    public String CAR_WASH = "car_wash";
    public String CASINO = "casino";
    public String CEMETERY = "cemetery";
    public String CHURCH = "church";
    public String CITY_HALL = "city_hall";
    public String CLOTHING_STORE = "clothing_store";
    public String CONVENIENCE_STORE = "convenience_store";
    public String COURTHOUSE = "courthouse";
    public String DENTIST = "dentist";
    public String DEPARTMENT_STORE = "department_store";
    public String DOCTOR = "doctor";
    public String ELECTRICIAN = "electrician";
    public String ELECTRONICS_STORE = "electronics_store";
    public String EMBASSY = "embassy";
    public String FINANCE = "finance";
    public String FIRE_STATION = "fire_station";
    public String FLORIST = "florist";
    public String FUNERAL_HOME = "funeral_home";
    public String FURNITURE_STORE = "furniture_store";
    public String GAS_STATION = "gas_station";
    public String GYM = "gym";
    public String HAIR_CARE = "hair_care";
    public String HARDWARE_STORE = "hardware_store";
    public String HINDU_TEMPLE = "hindu_temple";
    public String HOME_GOODS_STORE = "home_goods_store";
    public String HOSPITAL = "hospital";
    public String INSURANCE_AGENCY = "insurance_agency";
    public String JEWELRY_STORE = "jewelry_store";
    public String LAUNDRY = "laundry";
    public String LAWYER = "lawyer";
    public String LIBRARY = "library";
    public String LIQUOR_STORE = "liquor_store";
    public String LOCAL_GOVERNMENT_OFFICE = "local_government_office";
    public String LOCKSMITH = "locksmith";
    public String LODGING = "lodging";
    public String MEAL_DELIVERY = "meal_delivery";
    public String MEAL_TAKEAWAY = "meal_takeaway";
    public String MOSQUE = "mosque";
    public String MOVIE_RENTAL = "movie_rental";
    public String MOVIE_THEATER = "movie_theater";
    public String MOVING_COMPANY = "moving_company";
    public String MUSEUM = "museum";
    public String NIGHT_CLUB = "night_club";
    public String PAINTER = "painter";
    public String PARK = "park";
    public String PARKING = "parking";
    public String PET_STORE = "pet_store";
    public String PHARMACY = "pharmacy";
    public String PHYSIOTHERAPIST = "physiotherapist";
    public String PLUMBER = "plumber";
    public String POLICE = "police";
    public String POST_OFFICE = "post_office";
    public String REAL_ESTATE_AGENCY = "real_estate_agency";
    public String RESTAURANT = "restaurant";
    public String ROOFING_CONTRACTOR = "roofing_contractor";
    public String RV_PARK = "rv_park";
    public String SCHOOL = "school";
    public String SHOE_STORE = "shoe_store";
    public String SHOPPING_MALL = "shopping_mall";
    public String SPA = "spa";
    public String STADIUM = "stadium";
    public String STORAGE = "storage";
    public String STORE = "store";
    public String SUBWAY_STATION = "subway_station";
    public String SYNAGOGUE = "synagogue";
    public String TAXI_STAND = "taxi_stand";
    public String TRAIN_STATION = "train_station";
    public String TRANSIT_STATION = "transit_station";
    public String TRAVEL_AGENCY = "travel_agency";
    public String UNIVERSITY = "university";
    public String VETERINARY_CARE = "veterinary_care";
    public String ZOO = "zoo";

}



showPlaceInformation() 메소드에서 PlaceType.RESTAURANT를 필요시 위에 나온 원하는 타입으로 바꾸시면 됩니다. 

 

 

.type(PlaceType.RESTAURANT)를 지우면 주변에 있는 모든 타입의 장소가 검색됩니다.



5. 문제 발생시

로그캣에서 Place API 관련 주소를 찾을 수 있습니다. 

해당 주소를 복사하여  웹브라우저에 붙여넣기 해보세요.



다음과 같은 에러가 발생한다면..

{

   "error_message" : "You must enable Billing on the Google Cloud Project at https://console.cloud.google.com/project/_/billing/enable Learn more at https://developers.google.com/maps/gmp-get-started",

   "html_attributions" : [],

   "results" : [],

   "status" : "REQUEST_DENIED"

}



구글 클라우드 플랫폼에서 다음처럼 결재 수단을 선택하여 

신용 카드 또는 체크 카드를 등록해야 정상적으로 주변 장소 정보를 받을 수 있습니다. 

 

 

반응형

해보고 확인한 것을 문서화하여 기록합니다.


부족함이 있지만 도움이 되었으면 합니다.
잘못된 부분이나 개선점을 알려주시면 감사하겠습니다.



포스트 작성시에는 문제 없었지만 이후 문제가 생길 수 있습니다.
문제가 생기면 포스트와 바뀐 환경이 있나 먼저 확인해보세요.

질문을 남겨주면 가능한 빨리 답변드립니다.


제가 쓴 책도 한번 검토해보세요 ^^

  • 네이버 블러그 공유하기
  • 네이버 밴드에 공유하기
  • 페이스북 공유하기
  • 카카오스토리 공유하기

댓글을 달아 주세요

">
  1. 이전 댓글 더보기
  2. thumbnail
    익명
    2022.04.30 18:31

    비밀댓글입니다

  3. thumbnail
    익명
    2022.04.30 18:38

    비밀댓글입니다

  4. thumbnail
    익명
    2022.04.30 18:42

    비밀댓글입니다

  5. thumbnail
    익명
    2022.04.30 18:43

    비밀댓글입니다

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.04.30 18:47 신고

      로그캣의 로그를 보세요. 인증문제일 가능성이 있습니다.

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.04.30 18:48 신고

      아마도 주소가 하나 있을텐데 그걸 웹브라우저에 붙여넣기 해보세요. 인증오류나면 다시 발급받아야 합니다.

  6. thumbnail
    익명
    2022.04.30 19:45

    비밀댓글입니다

  7. thumbnail
    Favicon of https://2limit.tistory.com BlogIcon 2_limit

    안녕하세요 위에걸 그대로 했는데
    import noman.googleplaces.Place;
    import noman.googleplaces.PlacesException;
    import noman.googleplaces.PlacesListener;
    하고 밑에 noman에 빨간색이 뜨는데 어떻게 해결해야 하나요?
    오류에는 Cannot resolve symbol 'noman'이라 뜨네요...

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.15 15:18 신고

      다음부분을 확인해보세요

      Android-Google-Places-API 라이브러리를 추가해줍니다. 글 작성시점에서 최신 버전은 1.1.3입니다.
      변경된 사항을 반영해주기 위해 상단 우측에 보이는 Sync Now를 클릭합니다.

    • thumbnail
      Favicon of https://2limit.tistory.com BlogIcon 2_limit
      2022.05.15 15:40 신고

      제가 안드로이드 스튜디오가 처음이라 그러는데 라이브러리를 어떻게 추가하는건가요...?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.15 15:42 신고

      build.gradle 파일에 포스트에 표시된 패키지를 추가할수 있습니다.

    • thumbnail
      Favicon of https://2limit.tistory.com BlogIcon 2_limit
      2022.05.15 15:44 신고

      물론 build.gradle(:app)에
      dependencies {

      implementation 'noman.placesapi:placesAPI:1.1.3'

      }는 추가하였습니다

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.15 15:46 신고

      implementation있는 줄만 기존에 추가된거 아래에 추가했나요?

    • thumbnail
      Favicon of https://2limit.tistory.com BlogIcon 2_limit
      2022.05.15 15:49 신고

      네, 이렇게 되어있습니다.

      dependencies {

      implementation 'androidx.appcompat:appcompat:1.4.1'
      implementation 'com.google.android.material:material:1.6.0'
      implementation 'androidx.constraintlayout:constraintlayout:2.1.3'
      implementation 'com.google.android.gms:play-services-maps:18.0.2'
      implementation 'com.google.android.material:material:1.7.0-alpha01'
      implementation 'com.google.android.gms:play-services-location:19.0.1'
      implementation 'noman.placesapi:placesAPI:1.1.3'
      testImplementation 'junit:junit:4.13.2'
      androidTestImplementation 'androidx.test.ext:junit:1.1.3'
      androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
      }

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.15 15:50 신고

      원래는 그렇게 넣으면 되야하는데 문제가 발생한거 같네요. 빨간줄에 마우스 커서를 가져가면 이유를 보여줍니다.

    • thumbnail
      Favicon of https://2limit.tistory.com BlogIcon 2_limit
      2022.05.15 20:25 신고

      혹시 Android-Google-Places-API 라이브러리(https://github.com/nomanr/Android-Google-Places-API)이 깃허브 파일을 다운로드해야하나요?

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.15 20:27 신고

      원래는 build.gradle에만 적어주면 동작합니다.

  8. thumbnail
    익명
    2022.05.22 18:13

    비밀댓글입니다

  9. thumbnail
    익명
    2022.05.22 21:17

    비밀댓글입니다

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.22 21:28 신고

      구현에 사용한 라이브러리 문제면 해결이 안됩니다. 안드로이드 11에선 문제가 안되었던거 같습니다.

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.22 22:40 신고

      확인해보니 안드로이드 스튜디오 버전이 올라가면서 문제가 생긴듯합니다. 방법을 찾기가 쉽지 않네요...

  10. thumbnail
    익명
    2022.05.24 00:00

    비밀댓글입니다

  11. thumbnail
    익명
    2022.05.24 11:30

    비밀댓글입니다

  12. thumbnail
    익명
    2022.05.24 12:15

    비밀댓글입니다

  13. thumbnail
    익명
    2022.05.24 19:55

    비밀댓글입니다

  14. thumbnail
    익명
    2022.05.24 20:48

    비밀댓글입니다

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.24 22:25 신고

      확인해본바로는 최근 안드로이드 스튜디오 혹은 안드로이드 SDK때문에 동작안하는 듯합니다.

  15. thumbnail
    Favicon of https://thitwism.tistory.com BlogIcon doplink

    재능 기부해주셔서 너무 감사의 말씀드립니다. 코드를 작성하라는 대로 전부다 따라했는데, 'import noman'관련 모든 항목이 빨간줄과 함께 Can't resolve symbol 'noman' 이라고만 뜨네요.. 인터넷을 검색해봐도 나오지 않아 답답한데, 혹시 어떤 것이 문제일까요?
    implementation 'noman.placesapi:placesAPI:1.1.3' 를 gradle에 추가해서 sync할 때도 에러는 발생하지 않았습니다..ㅠ

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.24 22:45 신고

      최근 버전에서 문제가 생긴듯합니다. 안드로이드 스튜디오 혹은 안드로이드 sdk문제 같은데 아직 원인파악이 안되었습니다.

    • thumbnail
      Favicon of https://thitwism.tistory.com BlogIcon doplink
      2022.05.24 22:53 신고

      답변 너무 감사합니다! 그렇다면 어느 것의 버전을 낮춰야 실행이 될까요..?
      ________________________________________
      implementation 'androidx.appcompat:appcompat:1.4.1'
      implementation 'com.google.android.material:material:1.6.0'
      implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
      implementation 'com.google.android.gms:play-services-maps:18.0.2'
      implementation 'com.google.android.gms:play-services-location:19.0.1'
      implementation 'com.google.android.material:material:1.7.0-alpha01'
      implementation 'noman.placesapi:placesAPI:1.1.3'
      testImplementation 'junit:junit:4.13.2'
      androidTestImplementation 'androidx.test.ext:junit:1.1.3'
      androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
      _________________________________________
      현재 제 dependencies의 버전들 입니다!

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.25 07:16 신고

      안드로이드 스튜디오 버전과 sdk를 블로그 글에 적힌대로 바꾸면 동작할겁니다.

  16. thumbnail
    익명
    2022.05.25 10:31

    비밀댓글입니다

    • thumbnail
      Favicon of https://webnautes.tistory.com BlogIcon webnautes
      2022.05.25 12:16 신고

      build.gradle에서 sdk 버전 낮춰서 해봤는데 효과가 없었습니다. 안드로이드 스튜디오를 포스트 앞에 적어놓은 버전으로 바꿔보는게 좋을듯합니다.

  17. thumbnail
    익명
    2022.05.25 12:29

    비밀댓글입니다

  18. thumbnail
    익명
    2022.05.25 12:43

    비밀댓글입니다

  19. thumbnail
    익명
    2022.05.25 20:48

    비밀댓글입니다

  20. thumbnail
    익명
    2022.05.25 22:25

    비밀댓글입니다

  21. thumbnail
    Favicon of https://sosoplay.tistory.com BlogIcon t_Haru

    안되시는 분들 https://velog.io/@abc9985/Android-Failed-to-resolve-%EC%98%A4%EB%A5%98 페이지 참고해 보세요. 저는 이걸로 해결했습니다.