Анализ адресов_компонентов в Картах Google при выборе автозаполнения

У меня есть следующий код для синтаксического анализа country при выборе списка автозаполнения:

$('#spot_address').autocomplete({
  // This bit uses the geocoder to fetch address values
  source: function(request, response) {
    geocoder.geocode( {'address': request.term }, function(results, status) {
      // Get address_components
      for (var i = 0; i < results[0].address_components.length; i++)
      {
        var addr = results[0].address_components[i];
        var getCountry;
        if (addr.types[0] == 'country') 
          getCountry = addr.long_name;
      }
      response($.map(results, function(item) {
        return {
          label: item.formatted_address,
          value: item.formatted_address,
          latitude: item.geometry.location.lat(),
          longitude: item.geometry.location.lng(),
          country: getCountry
        }
      }));
    })
  },

  // This bit is executed upon selection of an address
  select: function(event, ui) {
    // Get values
    $('#spot_country').val(ui.item.country);
    $('#spot_lat').val(ui.item.latitude);
    $('#spot_lng').val(ui.item.longitude);
    var location = new google.maps.LatLng(ui.item.latitude, ui.item.longitude);
    marker.setPosition(location);
    map.setCenter(location);
  },

  // Changes the current marker when autocomplete dropdown list is focused
  focus: function(event, ui) {
    var location = new google.maps.LatLng(ui.item.latitude, ui.item.longitude);
    marker.setPosition(location);
    map.setCenter(location);
  }
});

Однако приведенный выше код не работает, и когда страна анализируется, анализируется только первый результат автозаполнения, независимо от того, что значимо для массива results[0], потому что он извлекает только первый результат.

Я попытался переместить его в функцию select, но ui в select содержит только formatted_address, longitude и latitude, но не address_components.

Что нужно сделать, чтобы отправить правильный country, когда выбран элемент списка автозаполнения?

Большое спасибо.

Ответ 1

Вот я снова отвечу на свой вопрос. Ниже приведен полный рабочий код:

$('#spot_address').autocomplete({
  // This bit uses the geocoder to fetch address values
  source: function(request, response) {
    geocoder.geocode( {'address': request.term }, function(results, status) {
      response($.map(results, function(item) {
          // Get address_components
          for (var i = 0; i < item.address_components.length; i++)
          {
            var addr = item.address_components[i];
            var getCountry;
            if (addr.types[0] == 'country') 
              getCountry = addr.long_name;
          }
        return {
          label: item.formatted_address,
          value: item.formatted_address,
          latitude: item.geometry.location.lat(),
          longitude: item.geometry.location.lng(),
          country: getCountry
        }
      }));
    })
  },

  // This bit is executed upon selection of an address
  select: function(event, ui) {
    // Get values
    $('#spot_country').val(ui.item.country);
    $('#spot_lat').val(ui.item.latitude);
    $('#spot_lng').val(ui.item.longitude);
    var location = new google.maps.LatLng(ui.item.latitude, ui.item.longitude);
    marker.setPosition(location);
    map.setCenter(location);
  },

  // Changes the current marker when autocomplete dropdown list is focused
  focus: function(event, ui) {
    var location = new google.maps.LatLng(ui.item.latitude, ui.item.longitude);
    marker.setPosition(location);
    map.setCenter(location);
  }
});

Ответ 2

Общее решение:

var address_components = results[0].address_components;
var components={}; 
jQuery.each(address_components, function(k,v1) {jQuery.each(v1.types, function(k2, v2){components[v2]=v1.long_name});});

Теперь ваш components выглядит следующим образом:

street_number: "1100", 
route: "E Hector St", 
locality: "Conshohocken", 
political: "United States", 
administrative_area_level_3: "Whitemarsh"…
administrative_area_level_1: "Pennsylvania"
administrative_area_level_2: "Montgomery"
administrative_area_level_3: "Whitemarsh"
country: "United States"
locality: "Conshohocken"
political: "United States"
postal_code: "19428"
route: "E Hector St"
street_number: "1100"

Что вы можете запросить следующим образом:

components.country

Ответ 3

Разработка на @Full Достойный ответ здесь версия для lodash:

_.each(address_components, function(k, v1) {
    _.each(address_components[v1].types, function(k2, v2){
        components[address_components[v1].types[v2]] = address_components[v1].long_name
    });
});

Ответ 4

В контроллере AngularJS это может быть так:

function NewController() {
  var vm = this;

  vm.address = null;
  vm.placeService  = null;

  activate();

  function activate() {
    vm.placeService = new google.maps.places.PlacesService(document.getElementById("map"));
  }

  function getAddressComponent(address, component, type) {
    var element = null;
    angular.forEach(address.address_components, function (address_component) {
      if (address_component.types[0] == component) {
        element = (type == 'short') ? address_component.short_name : address_component.long_name;
      }
    });

    return element;
  }

  function getAddressDetail(addressId) {
    var request = {
      placeId: addressId
    };

    vm.placeService.getDetails(request, function(place, status) {
      if (status == google.maps.places.PlacesServiceStatus.OK) {
        vm.address = {
          countryCode: getAddressComponent(place, 'country', 'short'),
          countryName: getAddressComponent(place, 'country', 'long'),
          cityCode: getAddressComponent(place, 'locality', 'short'),
          cityName: getAddressComponent(place, 'locality', 'long'),
          postalCode: getAddressComponent(place, 'postal_code', 'short'),
          streetNumber: getAddressComponent(place, 'street_number', 'short')
        };
        console.log(vm.address);
      }
    });
  }
}

Ответ 5

Вот мое решение в машинописи

interface AddressComponent {
  long_name: string;
  short_name: string;
  types: Array<string>;
}

interface Address {
  street_number?: string;
  street_name?: string;
  city?: string;
  state?: string;
  country?: string;
  postal_code?: string;
}

export class GoogleAddressParser {
  private address: Address = {};

  constructor(private address_components: Array<AddressComponent>) {
    this.parseAddress();
  }

  private parseAddress() {
    if (!Array.isArray(this.address_components)) {
      throw Error('Address Components is not an array');
    }

    if (!this.address_components.length) {
      throw Error('Address Components is empty');
    }

    for (let i = 0; i < this.address_components.length; i++) {
      const component: AddressComponent = this.address_components[i];

      if (this.isStreetNumber(component)) {
        this.address.street_number = component.long_name;
      }

      if (this.isStreetName(component)) {
        this.address.street_name = component.long_name;
      }

      if (this.isCity(component)) {
        this.address.city = component.long_name;
      }

      if (this.isCountry(component)) {
        this.address.country = component.long_name;
      }

      if  (this.isState(component)) {
        this.address.state = component.long_name;
      }

      if (this.isPostalCode(component)) {
        this.address.postal_code = component.long_name;
      }
    }
  }

  private isStreetNumber(component: AddressComponent): boolean {
    return component.types.includes('street_number');
  }

  private isStreetName(component: AddressComponent): boolean {
    return component.types.includes('route');
  }

  private isCity(component): boolean {
    return component.types.includes('locality');
  }

  private isState(component): boolean {
    return component.types.includes('administrative_area_level_1');
  }

  private isCountry(component): boolean {
    return component.types.includes('country');
  }

  private isPostalCode(component): boolean {
    return component.types.includes('postal_code');
  }

  result(): Address {
    return this.address;
  }
}

Использование:

const address = new GoogleAddressParser(results[0].address_components).result();

Ответ 6

Здесь я сделал собственное решение, так как хотел получить название города, и для этого может быть несколько форматов, например, название города в некоторых регионах может быть под названием

 (locality, sublocality , sublocality_level_1, sublocality_level_2, sublocality_level_3
 or sublocality_level_4)

поэтому я сделал эту функцию

getAddressObject(address_components) {
  var ShouldBeComponent = {
    home: ["street_number"],
    postal_code: ["postal_code"],
    street: ["street_address", "route"],
    region: [
      "administrative_area_level_1",
      "administrative_area_level_2",
      "administrative_area_level_3",
      "administrative_area_level_4",
      "administrative_area_level_5"
    ],
    city: [
      "locality",
      "sublocality",
      "sublocality_level_1",
      "sublocality_level_2",
      "sublocality_level_3",
      "sublocality_level_4"
    ],
    country: ["country"]
  };

  var address = {
    home: "",
    postal_code: "",
    street: "",
    region: "",
    city: "",
    country: ""
  };
  address_components.forEach(component => {
    for (var shouldBe in ShouldBeComponent) {
      if (ShouldBeComponent[shouldBe].indexOf(component.types[0]) !== -1) {
        address[shouldBe] = component.long_name;
      }
    }
  });
  console.log(address);
  return address;
}

Ответ 7

Это работало для меня, в AngularJS;

// Function converts GPS co-ordinates to a locality name
function showLocation(LatLng) {
    geocoder.geocode({'latLng': LatLng}, function (results, status) {
        if (status == google.maps.GeocoderStatus.OK) {
            console.log(results[0]);
            var myLocation;

            for (var i = 0; i < results[0].address_components.length; i++) {
                var addr = results[0].address_components[i];
                var getCountry;
                var getAdministrative;
                var getLocality;

                if (addr.types[0] == 'locality') {
                    getLocality = addr.long_name;
                    console.log(getLocality);
                    myLocation = getLocality+ ', ';
                }
                 if (addr.types[0] == 'administrative_area_level_1') {
                    getAdministrative = addr.long_name;
                    console.log(getAdministrative);
                    myLocation += getAdministrative + ', ';
                } 
                if (addr.types[0] == 'country') {
                    getCountry = addr.long_name;
                    console.log(getCountry);
                    myLocation += getCountry;
                }                             
            }
            $scope.locality = myLocation;
            console.log(myLocation);
        }
    })
};

Ответ 8

Я использовал следующий подход:

var city = getAddressComponent(place, 'locality', 'long_name');
var state = getAddressComponent(place, 'administrative_area_level_1', 'short_name');
var postalCode = getAddressComponent(place, 'postal_code', 'short_name');
var country = getAddressComponent(place, 'country', 'long_name');

function getAddressComponent(place, componentName, property) {
  var comps = place.address_components.filter(function(component) {
    return component.types.indexOf(componentName) !== -1;
  });

  if(comps && comps.length && comps[0] && comps[0][property]) {
    return comps[0][property];
  } else {
    return null;
  }
}

Ответ 9

Я думаю, вам нужно разделить обработчик ответа на новую функцию.

  source: function(request, response) {
    geocoder.geocode( {'address': request.term }, function(results, status) {
      // Get address_components
      for (var i = 0; i < results[0].address_components.length; i++)
      {
        var addr = results[0].address_components[i];
        var getCountry;
        if (addr.types[0] == 'country') 
          getCountry = addr.long_name;
      }
      response($.map(results, function(item) { getDetails(item); }));
    })
  },

Переместите это за пределы функции .autocomplete:

function getDetails(item) {
            return {
              label: item.formatted_address,
              value: item.formatted_address,
              latitude: item.geometry.location.lat(),
              longitude: item.geometry.location.lng(),
              country: getCountry
            }
          }

Ответ 10

Вот такая же функция, как Full Decent's, но написана для AngularJS:

function getAddressComponentByPlace(place) {
    var components;

    components = {};

    angular.forEach(place.address_components, function(address_component) {
        angular.forEach(address_component.types, function(type) {
            components[type] = address_component.long_name;
        });
    });

    return components;
}

Ответ 11

Страна всегда является последней в массиве, которая возвращается из Geocoder.

Вот мое решение -

 geocoder.geocode( {'address': request.term }, function(results, status) {
   var location_country =
   results[0].address_components[results[0].address_components.length - 1].long_name;
});

Надеюсь, что это поможет.

Ответ 12

Мы также можем извлечь идентификатор страны и код штата с помощью этого -

  const address_components = results[0].address_components;
  let components = {};
  address_components.map((value, index) => {
    value.types.map((value2, index2) => {
      components[value2] = value.long_name;
      if (value2==='country')
        components.country_id = value.short_name;
      if (value2==='administrative_area_level_1')
        components.state_code = value.short_name;
    })
  })

Возвращает этот объект -

{
  administrative_area_level_1: "California"
  administrative_area_level_2: "Santa Clara County"
  country: "United States"
  country_id: "US"
  locality: "Mountain View"
  political: "United States"
  postal_code: "94043"
  route: "Amphitheatre Parkway"
  state_code: "CA"
  street_number: "1600"
}

Ответ 13

Вот ES6 и JQuery менее раствор ( на основе William Entriken поста), что делает использование нативного reduce функции и DESTRUCTURING назначения синтаксиса для распаковки свойств из объектов, на отдельные переменные:

const address = address_components.reduce((seed, { long_name, types }) => {
  types.forEach(t => {
    seed[t] = long_name;
  });

  return seed;
}, {});

Или однострочная версия (для чего она стоит):

const address = address_components.reduce((seed, { long_name, types }) => (types.forEach(t => seed[t] = long_name), seed), {});

Что вы можете использовать как:

address.street_number
address.city