<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def build_geometry_by_shape( feed: "Feed", shape_ids: Optional[List[str]] = None, *, use_utm: bool = False, ) -> Dict: """ Return a dictionary with structure shape_id -> Shapely LineString of shape. Parameters feed : Feed shape_ids : list IDs of shapes in ``feed.shapes`` to restrict output to; return all shapes if ``None``. use_utm : boolean If ``True``, then use local UTM coordinates; otherwise, use WGS84 coordinates Returns ------- dictionary Has the structure shape_id -> Shapely LineString of shape. If ``feed.shapes is None``, then return ``None``. Return the empty dictionary if ``feed.shapes is None``. """

    if feed.shapes is None:
        return {}

    # Note the output for conversion to UTM with the utm package:
    # >>> u = utm.from_latlon(47.9941214, 7.8509671)
    # >>> print u
    # (414278, 5316285, 32, 'T')
    d = {}
    shapes = feed.shapes.copy()
    if shape_ids is not None:
        shapes = shapes[shapes["shape_id"].isin(shape_ids)]

    if use_utm:
        for shape, group in shapes.groupby("shape_id"):
            lons = group["shape_pt_lon"].values
            lats = group["shape_pt_lat"].values
            xys = [
                utm.from_latlon(lat, lon)[:2] for lat, lon in zip(lats, lons)
            ]
            d[shape] = sg.LineString(xys)
    else:
        for shape, group in shapes.groupby("shape_id"):
            lons = group["shape_pt_lon"].values
            lats = group["shape_pt_lat"].values
            lonlats = zip(lons, lats)
            d[shape] = sg.LineString(lonlats)
    return d