Return a location point by address.

        :param query: The address, query or a structured query
            you wish to geocode.

            .. versionchanged:: 1.0.0
                For a structured query, provide a dictionary whose keys
                are one of: `street`, `city`, `county`, `state`, `country`, or
                `postalcode`. For more information, see Nominatim's
                documentation for `structured requests`:

                    https://wiki.openstreetmap.org/wiki/Nominatim

        :type query: dict or str

        :param bool exactly_one: Return one result or a list of results, if
            available.

        :param int timeout: Time, in seconds, to wait for the geocoding service
            to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
            exception. Set this only if you wish to override, on this call
            only, the value set during the geocoder's initialization.

        :param int limit: Maximum amount of results to return from Nominatim.
            Unless exactly_one is set to False, limit will always be 1.

            .. versionadded:: 1.13.0

        :param bool addressdetails: If you want in *Location.raw* to include
            addressdetails such as city_district, etc set it to True

        :param str language: Preferred language in which to return results.
            Either uses standard
            `RFC2616 <http://www.ietf.org/rfc/rfc2616.txt>`_
            accept-language string or a simple comma-separated
            list of language codes.

            .. versionadded:: 1.0.0

        :param str geometry: If present, specifies whether the geocoding
            service should return the result's geometry in `wkt`, `svg`,
            `kml`, or `geojson` formats. This is available via the
            `raw` attribute on the returned :class:`geopy.location.Location`
            object.

            .. versionadded:: 1.3.0

        :param bool extratags: Include additional information in the result if available,
            e.g. wikipedia link, opening hours.

            .. versionadded:: 1.17.0

        :param country_codes: Limit search results
            to a specific country (or a list of countries).
            A country_code should be the ISO 3166-1alpha2 code,
            e.g. ``gb`` for the United Kingdom, ``de`` for Germany, etc.

            .. versionadded:: 1.19.0

        :type country_codes: str or list

        :type viewbox: list or tuple of 2 items of :class:`geopy.point.Point` or
            ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``.

        :param viewbox: Coordinates to restrict search within.
            Example: ``[Point(22, 180), Point(-22, -180)]``.

            .. versionadded:: 1.19.0

        :param bool bounded: Restrict the results to only items contained
            within the bounding view_box. Defaults to `False`.

            .. versionadded:: 1.19.0

        :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if
            ``exactly_one=False``.

def geocode(
            self,
            query,
            exactly_one=True,
            timeout=DEFAULT_SENTINEL,
            limit=None,
            addressdetails=False,
            language=False,
            geometry=None,
            extratags=False,
            country_codes=None,
            viewbox=None,
            bounded=None,  # TODO: change default value to `False` in geopy 2.0
    ):
        """
        Return a location point by address.

        :param query: The address, query or a structured query
            you wish to geocode.

            .. versionchanged:: 1.0.0
                For a structured query, provide a dictionary whose keys
                are one of: `street`, `city`, `county`, `state`, `country`, or
                `postalcode`. For more information, see Nominatim's
                documentation for `structured requests`:

                    https://wiki.openstreetmap.org/wiki/Nominatim

        :type query: dict or str

        :param bool exactly_one: Return one result or a list of results, if
            available.

        :param int timeout: Time, in seconds, to wait for the geocoding service
            to respond before raising a :class:`geopy.exc.GeocoderTimedOut`
            exception. Set this only if you wish to override, on this call
            only, the value set during the geocoder's initialization.

        :param int limit: Maximum amount of results to return from Nominatim.
            Unless exactly_one is set to False, limit will always be 1.

            .. versionadded:: 1.13.0

        :param bool addressdetails: If you want in *Location.raw* to include
            addressdetails such as city_district, etc set it to True

        :param str language: Preferred language in which to return results.
            Either uses standard
            `RFC2616 <http://www.ietf.org/rfc/rfc2616.txt>`_
            accept-language string or a simple comma-separated
            list of language codes.

            .. versionadded:: 1.0.0

        :param str geometry: If present, specifies whether the geocoding
            service should return the result's geometry in `wkt`, `svg`,
            `kml`, or `geojson` formats. This is available via the
            `raw` attribute on the returned :class:`geopy.location.Location`
            object.

            .. versionadded:: 1.3.0

        :param bool extratags: Include additional information in the result if available,
            e.g. wikipedia link, opening hours.

            .. versionadded:: 1.17.0

        :param country_codes: Limit search results
            to a specific country (or a list of countries).
            A country_code should be the ISO 3166-1alpha2 code,
            e.g. ``gb`` for the United Kingdom, ``de`` for Germany, etc.

            .. versionadded:: 1.19.0

        :type country_codes: str or list

        :type viewbox: list or tuple of 2 items of :class:`geopy.point.Point` or
            ``(latitude, longitude)`` or ``"%(latitude)s, %(longitude)s"``.

        :param viewbox: Coordinates to restrict search within.
            Example: ``[Point(22, 180), Point(-22, -180)]``.

            .. versionadded:: 1.19.0

        :param bool bounded: Restrict the results to only items contained
            within the bounding view_box. Defaults to `False`.

            .. versionadded:: 1.19.0

        :rtype: ``None``, :class:`geopy.location.Location` or a list of them, if
            ``exactly_one=False``.

        """

        if isinstance(query, dict):
            params = {
                key: val
                for key, val
                in query.items()
                if key in self.structured_query_params
            }
        else:
            params = {'q': self.format_string % query}

        params.update({
            'format': 'json'
        })

        if exactly_one:
            params['limit'] = 1
        elif limit is not None:
            limit = int(limit)
            if limit < 1:
                raise ValueError("Limit cannot be less than 1")
            params['limit'] = limit

        if viewbox is None:
            viewbox = self.view_box
        if viewbox:
            if len(viewbox) == 4:
                warnings.warn(
                    '%s `viewbox` format of '
                    '`[longitude, latitude, longitude, latitude]` is now '
                    'deprecated and will not be supported in geopy 2.0. '
                    'Use `[Point(latitude, longitude), Point(latitude, longitude)]` '
                    'instead.' % type(self).__name__,
                    DeprecationWarning,
                    stacklevel=2
                )
                lon1, lat1, lon2, lat2 = viewbox
                viewbox = [[lat1, lon1], [lat2, lon2]]
            params['viewbox'] = self._format_bounding_box(
                viewbox, "%(lon1)s,%(lat1)s,%(lon2)s,%(lat2)s")

        if bounded is None:
            bounded = self.bounded
        if bounded:
            params['bounded'] = 1

        if country_codes is None:
            country_codes = self.country_bias
        if not country_codes:
            country_codes = []
        if isinstance(country_codes, string_compare):
            country_codes = [country_codes]
        if country_codes:
            params['countrycodes'] = ",".join(country_codes)

        if addressdetails:
            params['addressdetails'] = 1

        if language:
            params['accept-language'] = language

        if extratags:
            params['extratags'] = True

        if geometry is not None:
            geometry = geometry.lower()
            if geometry == 'wkt':
                params['polygon_text'] = 1
            elif geometry == 'svg':
                params['polygon_svg'] = 1
            elif geometry == 'kml':
                params['polygon_kml'] = 1
            elif geometry == 'geojson':
                params['polygon_geojson'] = 1
            else:
                raise GeocoderQueryError(
                    "Invalid geometry format. Must be one of: "
                    "wkt, svg, kml, geojson."
                )

        url = self._construct_url(self.api, params)
        logger.debug("%s.geocode: %s", self.__class__.__name__, url)

        return self._parse_json(
            self._call_geocoder(url, timeout=timeout), exactly_one
        )