【问题标题】:Google Places Api sort by distanceGoogle Places Api 按距离排序
【发布时间】:2014-05-08 17:14:42
【问题描述】:

目前正在开发一个Android 应用程序,该应用程序返回最接近用户当前位置的 20 个位置。

Google Places API 正在返回接近用户位置的约 20 个位置,但不是按距离排序的 最近 20 个位置。

查看Google Places API Documentation 并没有显示我认为不正确的任何内容。

GetPlaces.java

String types = "accounting|airport|amusement_park|aquarium|art_gallery|atm|bakery|bank|bar|beauty_salon|bicycle_store|book_store|bowling_alley|bus_station|cafe|campground|car_dealer|car_rental|car_repair|car_wash|casino|cemetery|church|city_hall|clothing_store|convenience_store|courthouse|dentist|department_store|doctor|electrician|electronics_store|embassy|establishment|finance|fire_station|florist|food|funeral_home|furniture_store|gas_station|general_contractor|grocery_or_supermarket|gym|hair_care|hardware_store|health|hindu_temple|home_goods_store|hospital|insurance_agency|jewelry_store|laundry|lawyer|library|liquor_store|local_government_office|locksmith|lodging|meal_delivery|meal_takeaway|mosque|movie_rental|movie_theater|moving_company|museum|night_club|painter|park|parking|pet_store|pharmacy|physiotherapist|place_of_worship|plumber|police|post_office|real_estate_agency|restaurant|roofing_contractor|rv_park|school|shoe_store|shopping_mall|spa|stadium|storage|store|subway_station|synagogue|taxi_stand|train_station|travel_agency|university|veterinary_care|zoo";
resourceURI = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location="+myLocation.latitude+","+myLocation.longitude+"&radius=500&rankBy=distance&types="+ URLEncoder.encode(types, "UTF-8")+"&sensor=true&key=GOOGLE_MAPS_KEY";
try {
            String url =resourceURI; //getURL(myLocation.latitude,myLocation.longitude);

            HttpParams httpParams = new BasicHttpParams();
            HttpConnectionParams.setConnectionTimeout(httpParams, 30000);
            HttpConnectionParams.setSoTimeout(httpParams, 30000);
            DefaultHttpClient httpClient = new DefaultHttpClient(httpParams);
            HttpGet httpGet = new HttpGet(url);
            httpGet.setHeader("Content-type", "application/json");
            ResponseHandler responseHandler = new BasicResponseHandler();
            String response = (String) httpClient.execute(httpGet, responseHandler);

            if (response != null) {
                mResult = new JSONObject(response);
                results = mResult.getJSONArray("results");
            }
        }
        catch (ClientProtocolException e) {
            e.printStackTrace();
            return null;
        }
        catch (IOException e) {
            e.printStackTrace();
            return null;
        }
        catch (JSONException e) {
            e.printStackTrace();
            return null;
        }

        return results;
    }

这会返回有效的 JSON,但不会返回与传入的 distance 最接近的位置。我知道有一个比请求返回的地方更近的地方。

例如,我在一个已知的 google 地点提出请求,但它没有显示我当前所在的地点,而是显示更远的其他地点。

【问题讨论】:

  • 我看到这是一个旧线程,但是 API 选择问题和按距离顺序检索地点仍然是相关的。 @Raul 注意到对结果进行排序所需的唯一更改,但我很好奇您在 Android 应用程序中用于 Web 服务的 API 密钥类型。受 IP 地址保护的服务器密钥不适用于移动应用程序,因为每部手机都有唯一的 IP。 Android 密钥不起作用,因为该应用正在模拟浏览器请求。你最终做了什么?

标签: android google-api google-places-api


【解决方案1】:

希望这段代码能正常工作..

    private ArrayList<CLPlaceDTO> sortLocation(LatLng currentLatLng, ArrayList<?> alLocationDTO)
    {
        ArrayList<CLPlaceDTO> clPlaceDTOArrayList;
        ArrayList<CLPlaceDTO> clPlaceDTOArrayList1 = new ArrayList<>();
        double dCurrentLat = currentLatLng.latitude;
        double dCurrentLong = currentLatLng.longitude;

        Iterator<?> clIterator = alLocationDTO.iterator();
        while (clIterator.hasNext())
        {
            clIterator.next();
            clPlaceDTOArrayList = new ArrayList<>();
            for (int j = 0; j < alLocationDTO.size(); j++)
            {
                CLLocationDTO clLocationDTO = (CLLocationDTO) alLocationDTO.get(j);
                double dLat = clLocationDTO.getLatitude().doubleValue();
                double dLng = clLocationDTO.getLongitude().doubleValue();
                LatLng clNewLatLng = new LatLng(dLat, dLng);
                double dDistance = getDistance(dCurrentLat, dCurrentLong, dLat, dLng);
                CLPlaceDTO clPlaceDTO = new CLPlaceDTO(clLocationDTO.getAccountName(), clNewLatLng, dDistance);
                clPlaceDTOArrayList.add(clPlaceDTO);
            }
            Collections.sort(clPlaceDTOArrayList, new CLSortPlaces(currentLatLng));
            dCurrentLat = clPlaceDTOArrayList.get(0).getLatlng().latitude;
            dCurrentLong = clPlaceDTOArrayList.get(0).getLatlng().longitude;
            clPlaceDTOArrayList1.add(clPlaceDTOArrayList.get(0));
            clIterator.remove();
        }

        return clPlaceDTOArrayList1;
    }

     public static double getDistance(double dbFromLatitude,double dbFromLongitude,double dbToLatitude,double dbToLongitude)
    {
          double dbRadiusMeters = EARTH_RADIUS * 1000 ; // Earth’s mean radius in meter
          double dbLatitudeDiff = Math.toRadians(dbToLatitude - dbFromLatitude);
          double dbLongitudeDiff = Math.toRadians(dbToLongitude - dbFromLongitude);

          double a = Math.sin(dbLatitudeDiff / 2) * Math.sin(dbLatitudeDiff / 2) +
                                    Math.cos(Math.toRadians(dbFromLatitude)) * Math.cos(Math.toRadians(dbToLatitude)) *
                                    Math.sin(dbLongitudeDiff / 2) * Math.sin(dbLongitudeDiff / 2);

          double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
          double d = dbRadiusMeters * c;
          return d; // returns the distance in meter
    }

    public class CLSortPlaces implements Comparator<CLPlaceDTO>
    {
        private LatLng currentLoc;

        CLSortPlaces(LatLng current)
        {
            currentLoc = current;
        }

        @Override
        public int compare(final CLPlaceDTO place1, final CLPlaceDTO place2)
        {

            return (int) (place1.dDistance - place2.dDistance);
        }
    }



     public class CLPlaceDTO
{
    public LatLng latlng;
    public String sNameOfLocation;
    public double dDistance;

    public CLPlaceDTO(String sNameOfLocation, LatLng latlng,double dDistance)
    {
        this.sNameOfLocation = sNameOfLocation;
        this.latlng = latlng;
        this.dDistance=dDistance;
    }
    public CLPlaceDTO(String sNameOfLocation, LatLng latlng)
    {
        this.sNameOfLocation = sNameOfLocation;
        this.latlng = latlng;
        this.dDistance=dDistance;
    }}

【讨论】:

    【解决方案2】:

    也许你已经解决了你的问题,但我希望这可以帮助(关注粗体字)

    半径 - 定义返回位置结果的距离(以米为单位)。最大允许半径为 50 000 米。 注意,如果指定了 rankby = distance(在下面的可选参数下描述),则不能包含半径。

    rankby — 指定列出结果的顺序。可能的值是:

    突出(默认)。此选项根据结果的重要性对结果进行排序。排名将有利于指定区域内的突出位置。知名度可能会受到地点在 Google 索引中的排名、您的应用程序的签到次数、全球受欢迎程度以及其他因素的影响。

    距离。此选项按结果与指定位置的距离升序排序。指定距离时,需要一个或多个关键字、名称或类型。

    根据:https://developers.google.com/places/documentation/search?hl=en

    我根据google文档了解到,您不能同时发送参数“rankby”和“radius”,您必须同时只使用其中一个,这样您将获得按距离排序的结果。

    测试这个请求:

    resourceURI = "https://maps.googleapis.com/maps/api/place/nearbysearch/json?
    location="+myLocation.latitude+","+myLocation.longitude+"&rankBy=distance
    &types="+ URLEncoder.encode (types, "UTF-8") + "&sensor = true&
    key = GOOGLE_MAPS_KEY";
    

    看看你的表现,祝你好运!

    【讨论】:

    • 只是一个提示(可能是给未来的我):通过自己创建意图并使用intent.putExtra("rankBy", "distance"),它不会起作用。
    【解决方案3】:

    您认为比返回的地点更近的地点很可能不是真正的 Google 地点。

    查看此常见问题解答,让您的业务出现在 Google 地图上:https://support.google.com/places/answer/145585?hl=en

    对此的一个测试可能是尝试将您认为与 Google 地方https://www.google.com/local/business/add?hl=en&migratedId=04614545856067425787 更接近的企业注册并查看它是否允许您。如果可以,这意味着它们不是真正的 Google 地方,不会出现在此结果集中。

    【讨论】:

    • 嘿@Karios,事实上,我确实验证了我期待的地点在 Google Places 上。您还有其他想法吗?