'DRY routine for GeoQuerySet distance attribute routines.'

def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs):

    (procedure_args, geo_field) = self._spatial_setup(func, field_name=kwargs.get('field_name', None))
    connection = connections[self.db]
    geodetic = geo_field.geodetic(connection)
    geography = geo_field.geography
    if geodetic:
        dist_att = 'm'
    else:
        dist_att = Distance.unit_attname(geo_field.units_name(connection))
    distance = (func == 'distance')
    length = (func == 'length')
    perimeter = (func == 'perimeter')
    if (not (distance or length or perimeter)):
        raise ValueError(('Unknown    distance    function:    %s' % func))
    geom_3d = (geo_field.dim == 3)
    lookup_params = [(geom or 'POINT    (0    0)'), 0]
    backend = connection.ops
    if (spheroid or (backend.postgis and geodetic and (not geography) and length)):
        lookup_params.append('spheroid')
    lookup_params = geo_field.get_prep_value(lookup_params)
    params = geo_field.get_db_prep_lookup('distance_lte', lookup_params, connection=connection)
    geom_args = bool(geom)
    if backend.oracle:
        if distance:
            procedure_fmt = '%(geo_col)s,%(geom)s,%(tolerance)s'
        elif (length or perimeter):
            procedure_fmt = '%(geo_col)s,%(tolerance)s'
        procedure_args['tolerance'] = tolerance
    else:
        if self.query.transformed_srid:
            (u, unit_name, s) = get_srid_info(self.query.transformed_srid, connection)
            geodetic = (unit_name in geo_field.geodetic_units)
        if (backend.spatialite and geodetic):
            raise ValueError('SQLite    does    not    support    linear    distance    calculations    on    geodetic    coordinate    systems.')
        if distance:
            if self.query.transformed_srid:
                geom_args = False
                procedure_fmt = ('%s(%%(geo_col)s,    %s)' % (backend.transform, self.query.transformed_srid))
                if ((geom.srid is None) or (geom.srid == self.query.transformed_srid)):
                    if backend.spatialite:
                        procedure_fmt += (',    %s(%%%%s,    %s)' % (backend.from_text, self.query.transformed_srid))
                    else:
                        procedure_fmt += ',    %%s'
                elif backend.spatialite:
                    procedure_fmt += (',    %s(%s(%%%%s,    %s),    %s)' % (backend.transform, backend.from_text, geom.srid, self.query.transformed_srid))
                else:
                    procedure_fmt += (',    %s(%%%%s,    %s)' % (backend.transform, self.query.transformed_srid))
            else:
                procedure_fmt = '%(geo_col)s,%(geom)s'
            if ((not geography) and geodetic):
                if (not backend.geography):
                    if (not isinstance(geo_field, PointField)):
                        raise ValueError('Spherical    distance    calculation    only    supported    on    PointFields.')
                    if (not (str(Geometry(buffer(params[0].ewkb)).geom_type) == 'Point')):
                        raise ValueError('Spherical    distance    calculation    only    supported    with    Point    Geometry    parameters')
                if spheroid:
                    procedure_fmt += ",'%(spheroid)s'"
                    procedure_args.update({'function': backend.distance_spheroid, 'spheroid': params[1]})
                else:
                    procedure_args.update({'function': backend.distance_sphere})
        elif (length or perimeter):
            procedure_fmt = '%(geo_col)s'
            if ((not geography) and geodetic and length):
                procedure_fmt += ",'%(spheroid)s'"
                procedure_args.update({'function': backend.length_spheroid, 'spheroid': params[1]})
            elif (geom_3d and backend.postgis):
                if perimeter:
                    procedure_args.update({'function': backend.perimeter3d})
                elif length:
                    procedure_args.update({'function': backend.length3d})
    s = {'select_field': DistanceField(dist_att), 'setup': False, 'geo_field': geo_field, 'procedure_args': procedure_args, 'procedure_fmt': procedure_fmt}
    if geom_args:
        s['geom_args'] = ('geom',)
        s['procedure_args']['geom'] = geom
    elif geom:
        s['select_params'] = [backend.Adapter(geom)]
    return self._spatial_attribute(func, s, **kwargs)
