def _geocode_query_to_gdf(
    query: str | dict[str, str],
    which_result: int | None,
    by_osmid: bool,  # noqa: FBT001
) -> gpd.GeoDataFrame:
    """
    Geocode a single place query to a GeoDataFrame.

    Parameters
    ----------
    query
        Query string or structured dict to geocode.
    which_result
        Which search result to return. If None, auto-select the first
        (Multi)Polygon or raise an error if OSM doesn't return one. To get
        the top match regardless of geometry type, set `which_result=1`.
        Ignored if `by_osmid=True`.
    by_osmid
        If True, treat query as an OSM ID lookup rather than text search.

    Returns
    -------
    gdf
        GeoDataFrame with one row containing the geocoding result.
    """
    limit = 50 if which_result is None else which_result
    results = _nominatim._download_nominatim_element(query, by_osmid=by_osmid, limit=limit)

    # ensure geocoder results are sorted from most to least important
    results = sorted(results, key=lambda x: x["importance"], reverse=True)

    # choose the right result from the JSON response
    if len(results) == 0:
        # if no results were returned, raise error
        msg = f"Nominatim geocoder returned 0 results for query {query!r}."
        raise InsufficientResponseError(msg)

    if by_osmid:
        # if searching by OSM ID, always take the first (ie, only) result
        result = results[0]

    elif which_result is None:
        # else, if which_result=None, auto-select the first (Multi)Polygon
        try:
            result = _get_first_polygon(results)
        except TypeError as e:
            msg = f"Nominatim did not geocode query {query!r} to a geometry of type (Multi)Polygon."
            raise TypeError(msg) from e

    elif len(results) >= which_result:
        # else, if we got at least which_result results, choose that one
        result = results[which_result - 1]

    else:  # pragma: no cover
        # else, we got fewer results than which_result, raise error
        msg = f"Nominatim returned {len(results)} result(s) but `which_result={which_result}`."
        raise InsufficientResponseError(msg)

    # if we got a non (Multi)Polygon geometry type (like a point), log warning
    geom_type = result["geojson"]["type"]
    if geom_type not in {"Polygon", "MultiPolygon"}:
        msg = f"Nominatim geocoder returned a {geom_type} as the geometry for query {query!r}"
        utils.log(msg, level=lg.WARNING)

    # build the GeoJSON feature from the chosen result
    bottom, top, left, right = result["boundingbox"]
    feature = {
        "type": "Feature",
        "geometry": result["geojson"],
        "properties": {
            "bbox_west": left,
            "bbox_south": bottom,
            "bbox_east": right,
            "bbox_north": top,
        },
    }

    # add the other attributes we retrieved
    for attr in result:
        if attr not in {"address", "boundingbox", "geojson", "icon", "licence"}:
            feature["properties"][attr] = result[attr]

    # create and return the GeoDataFrame
    gdf = gpd.GeoDataFrame.from_features([feature])
    cols = ["lat", "lon", "bbox_north", "bbox_south", "bbox_east", "bbox_west"]
    gdf[cols] = gdf[cols].astype(float)
    return gdf

    Geocode a single place query to a GeoDataFrame.

    Parameters
    ----------
    query
        Query string or structured dict to geocode.
    which_result
        Which search result to return. If None, auto-select the first
        (Multi)Polygon or raise an error if OSM doesn't return one. To get
        the top match regardless of geometry type, set `which_result=1`.
        Ignored if `by_osmid=True`.
    by_osmid
        If True, treat query as an OSM ID lookup rather than text search.

    Returns
    -------
    gdf
        GeoDataFrame with one row containing the geocoding result.
    
osmnx/geocoder.py