def get_distance(self, dist_val, lookup_type): """ Returns a distance number in units of the field. For example, if `D(km=1)` was passed in and the units of the field were in meters, then 1000 would be returned. """ # Getting the distance parameter and any options. if len(dist_val) == 1: dist, option = dist_val[0], None else: dist, option = dist_val if isinstance(dist, Distance): if self.geodetic: # Won't allow Distance objects w/DWithin lookups on PostGIS. if SpatialBackend.postgis and lookup_type == 'dwithin': raise TypeError('Only numeric values of degree units are allowed on geographic DWithin queries.') # Spherical distance calculation parameter should be in meters. dist_param = dist.m else: dist_param = getattr(dist, Distance.unit_attname(self._unit_name)) else: # Assuming the distance is in the units of the field. dist_param = dist if SpatialBackend.postgis and self.geodetic and lookup_type != 'dwithin' and option == 'spheroid': # On PostGIS, by default `ST_distance_sphere` is used; but if the # accuracy of `ST_distance_spheroid` is needed than the spheroid # needs to be passed to the SQL stored procedure. return [gqn(self._spheroid), dist_param] else: return [dist_param]
def get_distance(self, f, dist_val, lookup_type): """ Retrieve the distance parameters for the given geometry field, distance lookup value, and the distance lookup type. This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the geography column type. """ # Getting the distance parameter value = dist_val[0] # Shorthand boolean flags. geodetic = f.geodetic(self.connection) geography = f.geography if isinstance(value, Distance): if geography: dist_param = value.m elif geodetic: if lookup_type == 'dwithin': raise ValueError('Only numeric values of degree units are ' 'allowed on geographic DWithin queries.') dist_param = value.m else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: # Assuming the distance is in the units of the field. dist_param = value return [dist_param]
def get_distance(self, value, lookup_type, connection): """ Returns a distance number in units of the field. For example, if `D(km=1)` was passed in and the units of the field were in meters, then 1000 would be returned. """ return connection.ops.get_distance(self, value, lookup_type) if isinstance(dist, Distance): if self.geodetic(connection): # Won't allow Distance objects w/DWithin lookups on PostGIS. if connection.ops.postgis and lookup_type == 'dwithin': raise ValueError('Only numeric values of degree units are allowed on geographic DWithin queries.') # Spherical distance calculation parameter should be in meters. dist_param = dist.m else: dist_param = getattr(dist, Distance.unit_attname(self.units_name(connection))) else: # Assuming the distance is in the units of the field. dist_param = dist if connection.ops.oracle and lookup_type == 'dwithin': dist_param = 'distance=%s' % dist_param if connection.ops.postgis and self.geodetic(connection) and lookup_type != 'dwithin' and option == 'spheroid': # On PostGIS, by default `ST_distance_sphere` is used; but if the # accuracy of `ST_distance_spheroid` is needed than the spheroid # needs to be passed to the SQL stored procedure. return [self._spheroid, dist_param] else: return [dist_param]
def distance_att(self, connection): dist_att = None if self.geo_field.geodetic(connection): if connection.features.supports_distance_geodetic: dist_att = 'm' else: units = self.geo_field.units_name(connection) if units: dist_att = DistanceMeasure.unit_attname(units) return dist_att
def get_distance_att_for_field(self, field): dist_att = None if field.geodetic(self.connection): if self.connection.features.supports_distance_geodetic: dist_att = 'm' else: units = field.units_name(self.connection) if units: dist_att = DistanceMeasure.unit_attname(units) return dist_att
def get_distance(self, f, value, lookup_type): value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): raise ValueError( 'Only numeric values of degree units are allowed on ' 'geodetic distance queries.' ) dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def convert_value(self, value, expression, connection, context): if value is None: return None geo_field = GeometryField(srid=self.srid) # Fake field to get SRID info if geo_field.geodetic(connection): dist_att = 'm' else: units = geo_field.units_name(connection) if units: dist_att = DistanceMeasure.unit_attname(units) else: dist_att = None if dist_att: return DistanceMeasure(**{dist_att: value}) return value
def get_distance(self, f, value, lookup_type): if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): raise ValueError('SAP HANA does not support distance queries on ' 'geometry fields with a geodetic coordinate system. ' 'Distance objects; use a numeric value of your ' 'distance in degrees instead.') else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def find_in_radius_as_geo_json(self, request): try: radius, point = get_radius_point_from_request(request) except: return Response( 'missing required parameter, radius, lat or lng is invalid', status=status.HTTP_400_BAD_REQUEST) filtered_locations = Location.objects.filter( point__distance_lt=(point, Distance(km=radius))) data = djangoSerializer('custom_geojson', filtered_locations, geometry_field='point', fields=('id', 'point', 'info', 'farm_id', 'location_type')) return Response(data)
def get_queryset(self): user = self.request.user lon = self.request.query_params.get('longitude') if lon is not None: lon = float(lon) lat = self.request.query_params.get('latitude') if lat is not None: lat = float(lat) rad = self.request.query_params.get('radius') if rad is not None: rad = float(rad) if lon is not None and lat is not None and rad is not None: collect_point_within_radius = CollectPoint.objects.filter( location__distance_lt=(Point(lon, lat), Distance(m=rad))) return collect_point_within_radius.filter(user=user) return CollectPoint.objects.filter(user=user)
def get_queryset(self): lat = self.request.query_params.get('lat') lon = self.request.query_params.get('lon') if not self.request.query_params.get( 'lat') or not self.request.query_params.get('lon'): raise ValidationError(detail='invalid request', code=status.HTTP_400_BAD_REQUEST) queryset = self.queryset if self.request.query_params.get('name'): queryset = queryset.filter( name__istartswith=self.request.query_params['name']) if self.request.query_params.get('is_active'): queryset = queryset.filter(is_active=True) queryset = queryset.filter( location__distance_lt=(f'Point({lat} {lon})', Distance(km=.2))) return queryset
def convert_value(self, value, expression, connection, context): if value is None: return None geo_field = GeometryField( srid=self.srid) # Fake field to get SRID info if geo_field.geodetic(connection): dist_att = 'm' else: units = geo_field.units_name(connection) if units: dist_att = DistanceMeasure.unit_attname(units) else: dist_att = None if dist_att: return DistanceMeasure(**{dist_att: value}) return value
def within_distance_from(self, lat, lng, **distance): '''Find shops within given distance from given point. Arguments: - lat: Latitude of reference point. - lng: Longitude of reference point. - **distance: `Distance` arguments. For supported units see: https://docs.djangoproject.com/en/2.1/ref/contrib/gis/measure/#supported-units Example: ``` shops = Shop.objects.within_distance_from(39.89, 22.18, km=10) ``` ''' return self.filter(coordinates__dwithin=(Point(lng, lat), Distance(**distance)))
def get_queryset(self): latitude = self.request.query_params.get('latitude', None) longitude = self.request.query_params.get('longitude', None) max_distance = self.request.query_params.get('max_distance', None) if latitude and longitude: point_of_user = Point(float(longitude), float(latitude), srid=4326) # Here we're actually doing the query, notice we're using the Distance class fom gis.measure queryset = Bike.objects.filter(location__distance_lte=( point_of_user, Distance(km=float(max_distance)))).annotate( distance_to_user=DistanceModel("location", point_of_user) ).order_by('distance_to_user') else: queryset = Bike.objects.all() return queryset
def jsonGen(result): bufferMAX = Distance(m=500) googlePoint = Point(result["location"]["lng"], result["location"]["lat"]) postcode = result["postcode"] vmPoint = AddressPoint.objects.filter(postcode=postcode).filter( geometry__distance_lt=( googlePoint, bufferMAX)).distance(googlePoint).order_by('distance').first() feature = {} if vmPoint: feature["geometry"] = vmPoint.prop.geometry.json feature["properties"] = { "id": vmPoint.prop.prop_pfi, "address": result['formatted_address'] } return feature
def handle(self, *args, **options): localities = Locality.objects.all() total = localities.count() for index, locality in enumerate(localities): data = locality.repr_dict() osm_type = None osm_id = None certainty = False print '%s/%s' % (index, total) try: # this is obviously from osm osm = data['source_url'].split( 'www.openstreetmap.org/')[1].split('/') osm_type = osm[0] osm_id = osm[1] certainty = True except (IndexError, KeyError): name = data['name'] # TODO: Update how to match this locality and osm # check by name match by_names = LocalityOSMView.objects.filter(name__iexact=name) name_match = True if by_names.count() >= 1 else False # check by name location by_geom = LocalityOSMView.objects.filter( geometry__distance_lt=(locality.geom, Distance(m=100))) if by_geom.count() == 0: by_geom = LocalityOSMView.objects.filter( geometry__contains=locality.geom) geom_match = True if by_geom.count() >= 1 else False # if geom match and name match, it is same locality # if name is not match, certainty is false if geom_match: osm_id = by_geom[0].osm_id osm_type = by_geom[0].osm_type if name_match: certainty = True print '%s : [%s , %s]' % (name, name_match, geom_match) instance, crt = LocalityHealthsitesOSM.objects.get_or_create( healthsite=locality) instance.osm_id = osm_id instance.osm_type = osm_type instance.certainty = certainty instance.save()
def filtered_gas_stations(update: Update, context: CallbackContext): chat_id = update.message.chat_id user, created = User.objects.get_or_create( external_id=chat_id, defaults={'username': update.message.from_user.username} ) context.user_data['user_id'] = chat_id if not user.active_filter: context.bot.send_message( chat_id=update.effective_chat.id, text='Сначала выберите активный фильтр' ) choose_active_filter(update, context, new=True) return point = Point(update.message.location.latitude, update.message.location.longitude) context.user_data['user_location'] = point active_filter = user.active_filter filtered_property = {} for field in GasStationProperties._meta.get_fields(): value = getattr(active_filter, field.name) if value: filtered_property[field.name] = value fuel_price_sq = Subquery(FuelPrice.objects. \ filter(gas_station=OuterRef('id')). \ filter(Q(fuel_type=active_filter.target_fuel) | Q(fuel_type__isnull=True)). \ order_by('-date').values('price')[:1]) search_radius = active_filter.search_radius gas_stations = [] #extend search radius until find at least one gas_station for r in range(active_filter.search_radius, active_filter.search_radius * 10): gas_stations = GasStation.objects.filter( location__distance_lt=(point, Distance(km=r)), **filtered_property). \ annotate(price=fuel_price_sq).order_by('price') if gas_stations.count(): break search_radius = r message = ('Выберите АЗС' if search_radius == active_filter.search_radius else f'В радиусе {active_filter.search_radius} км. нет АЗС. ' f'Радиус поиска расширен до {search_radius} км.') update.message.reply_text( text=message, reply_markup=choose_target_gas_station_keyboard(gas_stations) )
def get_queryset(self): latitude = self.request.query_params.get('latitude', None) longitude = self.request.query_params.get('longitude', None) max_distance = self.request.query_params.get('max_distance', None) if latitude and longitude: point_of_user = Point(float(longitude), float(latitude), srid=4326) # Here we're actually doing the query, notice we're using the Distance class fom gis.measure queryset =Package.objects.filter( location__distance_lte=( point_of_user, Distance(km=float(max_distance)) ) ) # .annotate( geom=Cast('location', PointField())).filter(geom__within=point_of_user) else: queryset =Package.objects.all() return queryset
def result2(request, lat, lon='33.00'): if float(lat) < -180 or float(lat) > 180: return HttpResponseBadRequest('Error 400') #return HttpResponse(status=400); #AEN: THIS DOESN'T WORK! Maybe it was, but was just sending an empty page. I WANT THE DEFAULT! #raise Http404; #This SHOULD be 400 pt = 'POINT(%s %s)' % (lon, lat) country = WorldBorder.objects.filter(mpoly__contains=pt) countriesNearUS = filter(lambda x: x.closeToUS(), WorldBorder.objects.all()) if not country: context = { 'lat': lat, 'lon': lon, 'countriesNearUnitedStates': countriesNearUS } return render(request, 'world/result.html', context) d = Distance(m=500) touching = WorldBorder.objects.filter(mpoly__touches=country[0].mpoly) neighbors = WorldBorder.objects.filter( mpoly__distance_lte=(country[0].mpoly, d)) neighbors = filter(lambda x: x.id != country[0].id, neighbors) t = tasks.getArea.delay(country[0].id) t.wait() #This should be something far more complicated, like a long pull,Perhaps USING rabbitmq to check based on the task.id! area = str(t.result) context = { 'country': country, 'lat': lat, 'lon': lon, 'touching': touching, 'neighbors': neighbors, 'distance': d, 'area': area, 'countriesNearUnitedStates': countriesNearUS } return render(request, 'world/result.html', context) '''if not country:
def get(self, request, format=None): radius = self.request.query_params.get('radius', None) point = self.request.query_params.get('point', None) bbox = self.request.query_params.get('bbox', None) from_date = self.request.query_params.get('from', None) to_date = self.request.query_params.get('to', None) user = self.request.query_params.get('user', None) phenomenon = self.request.query_params.get('phenomenon', None) phenomenon_id = self.request.query_params.get('phenomenon_id', None) category = self.request.query_params.get('category', None) category_id = self.request.query_params.get('category_id', None) observations = MetadataObservation.objects.all() if (radius and point) and len(point.split(',')) == 2: # value in meters longitude, latitude = point.split(',') point_within = Point(float(longitude), float(latitude)) observations = observations.filter( geometry__distance_lte=(point_within, Distance(m=radius))) if bbox and len(bbox.split(',')) == 4: # (xmin, ymin, xmax, ymax) observations = observations.filter( geometry__intersects=Polygon.from_bbox(bbox.split(','))) if from_date and to_date: observations = observations.filter( send_date__gte=datetime.strptime(from_date, '%Y-%m-%d'), send_date__lte=datetime.strptime(to_date, '%Y-%m-%d').replace( hour=23, minute=59, second=59)) if phenomenon: observations = observations.filter( values__phenomenon__name__iexact=phenomenon) if phenomenon_id: observations = observations.filter( values__phenomenon__id=phenomenon_id) if category: observations = observations.filter( values__phenomenon__category__name__iexact=category) if category_id: observations = observations.filter( values__phenomenon__category__id=category_id) observations = observations.distinct() paginator = PageNumberPagination() result_page = paginator.paginate_queryset(observations, request) serializer = MetadataObservationSerializer(result_page, many=True) return Response(serializer.data)
def get_homes_locations_near(lat=chicagolatlng[0], lng=chicagolatlng[1], radius=5) -> models.QuerySet: """Given `lat`, `lng`, and `radius`, return a list of dicts of [ { 'home': homes, 'location': homeLocation, 'distance': 1.23, }, (...) ] that are within `radius` miles of `lat`,`lng`. """ bpoint = Point(x=lat, y=lng) ret = [] # return dict dist = Distance(mi=radius) print(dist) # all within x miles homeLocations = HomeLocation.objects.filter( point__distance_lte=(bpoint, dist)) homeLocation: HomeLocation for homeLocation in homeLocations: # construct return dict homepoint = homeLocation.point print(bpoint) print(homepoint) dist = calc_dist_p_miles(bpoint, homepoint) ret.append({ 'home': homeLocation.get_home(), 'location': homeLocation, 'distance': dist, }) pprint(ret) return ret
def erp_details(request, commune, erp_slug, activite_slug=None): base_qs = (Erp.objects.select_related( "accessibilite", "activite", "commune_ext", "user", ).published().with_votes().filter(commune_ext__slug=commune, slug=erp_slug)) if activite_slug: base_qs = base_qs.filter(activite__slug=activite_slug) erp = get_object_or_404(base_qs) nearest_erps = (Erp.objects.select_related( "accessibilite", "activite", "commune_ext").published().nearest( erp.geom).filter(distance__lt=Distance(km=20))[:10]) geojson_list = make_geojson(nearest_erps) form = forms.ViewAccessibiliteForm(instance=erp.accessibilite) accessibilite_data = form.get_accessibilite_data() user_vote = (request.user.is_authenticated and not Vote.objects.filter(user=request.user, erp=erp).exists() and request.user != erp.user) user_is_subscribed = request.user.is_authenticated and erp.is_subscribed_by( request.user) url_widget_js = f"{settings.SITE_ROOT_URL}/static/js/widget.js" widget_tag = f"""<div id="widget-a11y-container" data-pk="{erp.uuid}" data-baseurl="{settings.SITE_ROOT_URL}"></div>\n <a href="#" aria-haspopup="dialog" aria-controls="dialog">Accessibilité</a> <script src="{url_widget_js}" type="text/javascript" async="true"></script>\n""" return render( request, "erp/index.html", context={ "accessibilite_data": accessibilite_data, "activite": erp.activite, "commune": erp.commune_ext, "commune_json": erp.commune_ext.toTemplateJson(), "erp": erp, "geojson_list": geojson_list, "nearest_erps": nearest_erps, "widget_tag": widget_tag, "url_widget_js": url_widget_js, "root_url": settings.SITE_ROOT_URL, "user_is_subscribed": user_is_subscribed, "user_vote": user_vote, }, )
def validate(self, attrs): if not attrs['lat'] or not attrs['lng']: raise serializers.ValidationError( "Dacă nu ești la turn, nu poți face provocarea!") if not attrs['tower']: raise serializers.ValidationError("Trebuie un turn!") point = Point(attrs['lng'], attrs['lat']) try: Tower.objects.filter(pk=attrs['tower'].id, location__distance_lt=(point, Distance(m=50))) except Tower.DoesNotExist: raise serializers.ValidationError( "Trebuie să fii la maxim 50 de metri de turn pentru a putea face provoarea!" ) return attrs
def buffer_geometry(geometry, extent_in_nm): """ Buffer geometry by projecting it to a coordinate system in meters. Buffer the geometry by extent_in_nm. Reproject the geometry back into WGS84 """ extent_in_m = Distance(nm=extent_in_nm).m projected = geometry.transform(PROJ_4326_TO_7314, clone=True) buffered = projected.buffer(extent_in_m) buffered.srid = 7314 unprojected = buffered.transform(PROJ_7314_TO_4326, clone=True) unprojected.srid = 4326 multipolygon = unwrap_multipolygon(MultiPolygon(unprojected)) multipolygon.srid = 4326 return multipolygon
def convert_values(self, value, field): """ Using the same routines that Oracle does we can convert our extra selection objects into Geometry and Distance objects. TODO: Laziness. """ if SpatialBackend.oracle: # Running through Oracle's first. value = super(GeoQuery, self).convert_values(value, field) if isinstance(field, DistanceField): # Using the field's distance attribute, can instantiate # `Distance` with the right context. value = Distance(**{field.distance_att: value}) elif isinstance(field, AreaField): value = Area(**{field.area_att: value}) elif isinstance(field, GeomField): value = SpatialBackend.Geometry(value) return value
def get_distance(self, f, value, lookup_type): if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): raise ValueError( 'SAP HANA does not support distance queries on ' 'geometry fields with a geodetic coordinate system. ' 'Distance objects; use a numeric value of your ' 'distance in degrees instead.') else: dist_param = getattr( value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def get_distance(self, f, dist_val, lookup_type, handle_spheroid=True): """ Retrieve the distance parameters for the given geometry field, distance lookup value, and the distance lookup type. This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the geography column type. """ # Getting the distance parameter value = dist_val[0] # Shorthand boolean flags. geodetic = f.geodetic(self.connection) geography = f.geography if isinstance(value, Distance): if geography: dist_param = value.m elif geodetic: if lookup_type == 'dwithin': raise ValueError('Only numeric values of degree units are ' 'allowed on geographic DWithin queries.') dist_param = value.m else: dist_param = getattr( value, Distance.unit_attname(f.units_name(self.connection))) else: # Assuming the distance is in the units of the field. dist_param = value params = [dist_param] # handle_spheroid *might* be dropped in Django 2.0 as PostGISDistanceOperator # also handles it (#25524). if handle_spheroid and len(dist_val) > 1: option = dist_val[1] if (not geography and geodetic and lookup_type != 'dwithin' and option == 'spheroid'): # using distance_spheroid requires the spheroid of the field as # a parameter. params.insert(0, f._spheroid) return params
def ride_search(request: HttpRequest) -> JsonResponse: try: post = json.loads(request.body) except json.JSONDecodeError: return JsonResponse('', status=status.HTTP_400_BAD_REQUEST, safe=False) query = Q() if 'latitude' not in post or 'longitude' not in post: return JsonResponse('', status=status.HTTP_400_BAD_REQUEST, safe=False) initial_point = Point(post['longitude'], post['latitude']) if 'title' in post: query |= Q(title__contains=post['title']) if 'start_loc_name' in post: query |= Q(start_loc__contains=post['start_loc_name']) if 'end_loc_name' in post: query |= Q(end_loc__contains=post['end_loc_name']) if 'max_start_dist' in post: max_dist = float(re.sub(r'\D', '', post['max_start_dist'])) if post['max_start_dist'][-1] == 'm': query &= Q(start_loc__distance_lt=(initial_point, Distance(m=max_dist))) elif post['max_start_dist'][-2:] == 'km': query &= Q(start_loc__distance_lt=(initial_point, Distance(km=max_dist))) elif post['max_start_dist'][-2:] == 'yd': query &= Q(start_loc__distance_lt=(initial_point, Distance(yd=max_dist))) elif post['max_start_dist'][-2:] == 'mi': query &= Q(start_loc__distance_lt=(initial_point, Distance(mi=max_dist))) else: print('invalid units in distance {}'.format(post['max_start_dist'])) if 'max_end_dist' in post: max_dist = float(re.sub(r'\D', '', post['max_end_dist'])) if post['max_end_dist'][-1] == 'm': query &= Q(end_loc__distance_lt=(initial_point, Distance(m=max_dist))) elif post['max_end_dist'][-2:] == 'km': query &= Q(end_loc__distance_lt=(initial_point, Distance(km=max_dist))) elif post['max_end_dist'][-2:] == 'yd': query &= Q(end_loc__distance_lt=(initial_point, Distance(yd=max_dist))) elif post['max_end_dist'][-2:] == 'mi': query &= Q(end_loc__distance_lt=(initial_point, Distance(mi=max_dist))) else: print('invalid units in distance {}'.format(post['max_end_dist'])) rides = Ride.objects.filter(query).all() return JsonResponse(serialize_public_rides(rides), safe=False)
def test_distance_search_api_result(self): c = Consumer.objects.all()[0] r = Report(product=Product.objects.all()[2], price=100, pnt='POINT(-200.1000 44.0489)', consumer=c) r.save() search = { "product_query": "product", 'pnt': 'POINT(-200.1000 44.0000)', 'distance': 100 } response = self.client.post('/reports/search', data=search).json() pnt = GEOSGeometry('POINT(-200.1000 44.0000)', srid=SRID) self.assertEqual( len(response['results']['features']), len( Report.objects.filter(pnt__distance_lte=(pnt, Distance(km=100)))))
def get(self, request): request_user = request.user profile = request_user.profile distance = profile.search_distance post = Post.objects.select_related('user').filter( ~Q(user=request_user) & Q(user__profile__location__distance_lt=(profile.location, Distance(km=distance))) & # TODO distance based on subscription ~Q(user__profile__in=profile.viewed.all())).first() if not post: return Response( data={ 'message': 'found no one around', 'tip': f"You can try to clear viewed users in your profile" }) profile.viewed.add(post.user.profile) serializer = PostSerializer(post, context={'request': request}) return Response(serializer.data)
def recommendations(request, n): radius = 5 current_user = User.objects.get(name=n) recommendations_list = User.objects.filter( location__dwithin=(current_user.location, Distance(km=radius))) latitude = [] longitude = [] names = [] for u in recommendations_list: if (u.name != n): latitude.append(u.location.y) longitude.append(u.location.x) names.append(u.name) context = {'latitude': latitude, 'longitude': longitude, 'names': names} return render(request, 'lex/recommendations.html', {"recommendations": json.dumps(context)}) # Create your views here.
def get_distance(self, f, dist_val, lookup_type): """ Retrieve the distance parameters for the given geometry field, distance lookup value, and the distance lookup type. This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the newly introduced geography column type introudced in PostGIS 1.5. """ # Getting the distance parameter and any options. if len(dist_val) == 1: value, option = dist_val[0], None else: value, option = dist_val # Shorthand boolean flags. geodetic = f.geodetic(self.connection) geography = f.geography and self.geography if isinstance(value, Distance): if geography: dist_param = value.m elif geodetic: if lookup_type == 'dwithin': raise ValueError('Only numeric values of degree units are ' 'allowed on geographic DWithin queries.') dist_param = value.m else: dist_param = getattr( value, Distance.unit_attname(f.units_name(self.connection))) else: # Assuming the distance is in the units of the field. dist_param = value if (not geography and geodetic and lookup_type != 'dwithin' and option == 'spheroid'): # using distance_spheroid requires the spheroid of the field as # a parameter. return [f._spheroid, dist_param] else: return [dist_param]
def get_distance(self, f, dist_val, lookup_type, handle_spheroid=True): """ Retrieve the distance parameters for the given geometry field, distance lookup value, and the distance lookup type. This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the geography column type. """ # Getting the distance parameter value = dist_val[0] # Shorthand boolean flags. geodetic = f.geodetic(self.connection) geography = f.geography if isinstance(value, Distance): if geography: dist_param = value.m elif geodetic: if lookup_type == 'dwithin': raise ValueError('Only numeric values of degree units are ' 'allowed on geographic DWithin queries.') dist_param = value.m else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: # Assuming the distance is in the units of the field. dist_param = value params = [dist_param] # handle_spheroid *might* be dropped in Django 2.0 as PostGISDistanceOperator # also handles it (#25524). if handle_spheroid and len(dist_val) > 1: option = dist_val[1] if (not geography and geodetic and lookup_type != 'dwithin' and option == 'spheroid'): # using distance_spheroid requires the spheroid of the field as # a parameter. params.insert(0, f._spheroid) return params
def filter_queryset(self, request, queryset, view): filter_field = getattr(view, 'distance_filter_field', None) radius = request.query_params.get(self.radius_param, None) point = self.get_filter_point(request) if not all((filter_field, radius, point)): return queryset # distance in meters try: dist = Distance(m=radius) except TypeError: raise ParseError( 'Invalid distance string supplied for parameter {0}'.format( self.radius_param)) return queryset.filter( Q(**{'{}__distance_lt'.format(filter_field): ( point, dist)})).annotate(distance=DistanceFunc( 'coordinates', Point(point.x, point.y, srid=4326)))
def _process_preexisting(self, location): erp = (Erp.objects.exclude(source=Erp.SOURCE_NESTENN).filter( activite=self.activite, geom__distance_lte=(location, Distance(m=2000)), ).first()) if erp: # unpublish already imported duplicate old_erp = Erp.objects.find_by_source_id( Erp.SOURCE_NESTENN, self.record["id"], published=True, ).first() if old_erp: old_erp.published = False old_erp.save() logger.info(f"Unpublished obsolete duplicate: {str(old_erp)}") # update preexisting erp with new import information erp.source = Erp.SOURCE_NESTENN erp.source_id = self.record["id"] return erp
def convert_values(self, value, field, connection): """ Using the same routines that Oracle does we can convert our extra selection objects into Geometry and Distance objects. TODO: Make converted objects 'lazy' for less overhead. """ if connection.ops.oracle: # Running through Oracle's first. value = super(GeoQuery, self).convert_values(value, field or GeomField(), connection) if isinstance(field, DistanceField): # Using the field's distance attribute, can instantiate # `Distance` with the right context. value = Distance(**{field.distance_att: value}) elif isinstance(field, AreaField): value = Area(**{field.area_att: value}) elif isinstance(field, (GeomField, GeometryField)) and value: value = Geometry(value) return value
def testInit(self): "Testing initialization from valid units" d = Distance(m=100) self.assertEqual(d.m, 100) d1, d2, d3 = D(m=100), D(meter=100), D(metre=100) for d in (d1, d2, d3): self.assertEqual(d.m, 100) d = D(nm=100) self.assertEqual(d.m, 185200) y1, y2, y3 = D(yd=100), D(yard=100), D(Yard=100) for d in (y1, y2, y3): self.assertEqual(d.yd, 100) mm1, mm2 = D(millimeter=1000), D(MiLLiMeTeR=1000) for d in (mm1, mm2): self.assertEqual(d.m, 1.0) self.assertEqual(d.mm, 1000.0)
def get_queryset(self): query_params = Q() categories_params = Q() exclude_dates_params = Q() categories = self.request.query_params.getlist('categories[]') latitude = (self.request.query_params.get('lat')) longitude = (self.request.query_params.get('lng')) distance = (self.request.query_params.get('miles')) min_price = (self.request.query_params.get('minPrice')) max_price = (self.request.query_params.get('maxPrice')) start_date = (self.request.query_params.get('startDate')) end_date = (self.request.query_params.get('endDate')) gear = (self.request.query_params.get('gear')) user = (self.request.query_params.get('user')) if latitude and longitude and distance: center = fromstr("POINT({} {})".format(latitude, longitude)) distance_from_point = {'mi': distance} query_params.add(Q(point__distance_lte=(center, Distance(**distance_from_point))), query_params.connector) if min_price: query_params.add(Q(gear__price__gte=min_price), query_params.connector) if max_price: query_params.add(Q(gear__price__lte=max_price), query_params.connector) if gear: query_params.add(Q(gear__id=gear), query_params.connector) if user: query_params.add(Q(gear__user__id=user), query_params.connector) if start_date: start = datetime.strptime(start_date, "%Y-%m-%d") exclude_dates_params.add(Q(gear__gearavailability__not_available_date__gte=start), query_params.connector) if end_date: end = datetime.strptime(end_date, "%Y-%m-%d") exclude_dates_params.add(Q(gear__gearavailability__not_available_date__lte=end), query_params.connector) for i in categories: categories_params.add(Q(gear__category__id=i), categories_params.OR) queryset = self.queryset.filter(query_params).filter(categories_params).exclude(exclude_dates_params)#.distance(center).order_by('distance') return queryset
def get_distance(self, f, dist_val, lookup_type): """ Retrieve the distance parameters for the given geometry field, distance lookup value, and the distance lookup type. This is the most complex implementation of the spatial backends due to what is supported on geodetic geometry columns vs. what's available on projected geometry columns. In addition, it has to take into account the newly introduced geography column type introudced in PostGIS 1.5. """ # Getting the distance parameter and any options. if len(dist_val) == 1: value, option = dist_val[0], None else: value, option = dist_val # Shorthand boolean flags. geodetic = f.geodetic(self.connection) geography = f.geography and self.geography if isinstance(value, Distance): if geography: dist_param = value.m elif geodetic: if lookup_type == 'dwithin': raise ValueError('Only numeric values of degree units are ' 'allowed on geographic DWithin queries.') dist_param = value.m else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: # Assuming the distance is in the units of the field. dist_param = value if (not geography and geodetic and lookup_type != 'dwithin' and option == 'spheroid'): # using distance_spheroid requires the spheroid of the field as # a parameter. return [f._spheroid, dist_param] else: return [dist_param]
def get_distance(self, f, value, lookup_type): """ Returns the distance parameters for the given geometry field, lookup value, and lookup type. This is based on the Spatialite backend, since we don't currently support geography operations. """ if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): raise ValueError('The SQL Server backend does not support ' 'distance queries on geometry fields with ' 'a geodetic coordinate system. Distance ' 'objects; use a numeric value of your ' 'distance in degrees instead.') else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def get_distance(self, f, value, lookup_type): """ Returns the distance parameters for the given geometry field, lookup value, and lookup type. SpatiaLite only supports regular cartesian-based queries (no spheroid/sphere calculations for point geometries like PostGIS). """ if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): raise ValueError('SpatiaLite does not support distance queries on ' 'geometry fields with a geodetic coordinate system. ' 'Distance objects; use a numeric value of your ' 'distance in degrees instead.') else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def get_distance(self, f, value, lookup_type, **kwargs): """ Return the distance parameters for the given geometry field, lookup value, and lookup type. """ if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): if lookup_type == 'dwithin': raise ValueError( 'Only numeric values of degree units are allowed on ' 'geographic DWithin queries.' ) dist_param = value.m else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def get_distance(self, f, value, lookup_type): """ Return the distance parameters for the given geometry field, lookup value, and lookup type. """ if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): if lookup_type == 'dwithin': raise ValueError( 'Only numeric values of degree units are allowed on ' 'geographic DWithin queries.' ) dist_param = value.m else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value return [dist_param]
def get_distance(self, f, value, lookup_type): """ Returns the distance parameters given the value and the lookup type. On Oracle, geometry columns with a geodetic coordinate system behave implicitly like a geography column, and thus meters will be used as the distance parameter on them. """ if not value: return [] value = value[0] if isinstance(value, Distance): if f.geodetic(self.connection): dist_param = value.m else: dist_param = getattr(value, Distance.unit_attname(f.units_name(self.connection))) else: dist_param = value # dwithin lookups on oracle require a special string parameter # that starts with "distance=". if lookup_type == 'dwithin': dist_param = 'distance=%s' % dist_param return [dist_param]
"""
import re
from decimal import Decimal
def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs): """ DRY routine for GeoQuerySet distance attribute routines. """ # Setting up the distance procedure arguments. procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name', None)) # If geodetic defaulting distance attribute to meters (Oracle and # PostGIS spherical distances return meters). Otherwise, use the # units of the geometry field. if geo_field.geodetic: dist_att = 'm' else: dist_att = Distance.unit_attname(geo_field._unit_name) # Shortcut booleans for what distance function we're using. distance = func == 'distance' length = func == 'length' perimeter = func == 'perimeter' if not (distance or length or perimeter): raise ValueError('Unknown distance function: %s' % func) # The field's get_db_prep_lookup() is used to get any # extra distance parameters. Here we set up the # parameters that will be passed in to field's function. lookup_params = [geom or 'POINT (0 0)', 0] # If the spheroid calculation is desired, either by the `spheroid` # keyword or wehn calculating the length of geodetic field, make # sure the 'spheroid' distance setting string is passed in so we # get the correct spatial stored procedure. if spheroid or (SpatialBackend.postgis and geo_field.geodetic and length): lookup_params.append('spheroid') where, params = geo_field.get_db_prep_lookup('distance_lte', lookup_params) # The `geom_args` flag is set to true if a geometry parameter was # passed in. geom_args = bool(geom) if SpatialBackend.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: # Getting whether this field is in units of degrees since the field may have # been transformed via the `transform` GeoQuerySet method. if self.query.transformed_srid: u, unit_name, s = get_srid_info(self.query.transformed_srid) geodetic = unit_name in geo_field.geodetic_units else: geodetic = geo_field.geodetic if distance: if self.query.transformed_srid: # Setting the `geom_args` flag to false because we want to handle # transformation SQL here, rather than the way done by default # (which will transform to the original SRID of the field rather # than to what was transformed to). geom_args = False procedure_fmt = '%s(%%(geo_col)s, %s)' % (SpatialBackend.transform, self.query.transformed_srid) if geom.srid is None or geom.srid == self.query.transformed_srid: # If the geom parameter srid is None, it is assumed the coordinates # are in the transformed units. A placeholder is used for the # geometry parameter. procedure_fmt += ', %%s' else: # We need to transform the geom to the srid specified in `transform()`, # so wrapping the geometry placeholder in transformation SQL. procedure_fmt += ', %s(%%%%s, %s)' % (SpatialBackend.transform, self.query.transformed_srid) else: # `transform()` was not used on this GeoQuerySet. procedure_fmt = '%(geo_col)s,%(geom)s' if geodetic: # Spherical distance calculation is needed (because the geographic # field is geodetic). However, the PostGIS ST_distance_sphere/spheroid() # procedures may only do queries from point columns to point geometries # some error checking is required. if not isinstance(geo_field, PointField): raise TypeError('Spherical distance calculation only supported on PointFields.') if not str(SpatialBackend.Geometry(buffer(params[0].wkb)).geom_type) == 'Point': raise TypeError('Spherical distance calculation only supported with Point Geometry parameters') # The `function` procedure argument needs to be set differently for # geodetic distance calculations. if spheroid: # Call to distance_spheroid() requires spheroid param as well. procedure_fmt += ',%(spheroid)s' procedure_args.update({'function' : SpatialBackend.distance_spheroid, 'spheroid' : where[1]}) else: procedure_args.update({'function' : SpatialBackend.distance_sphere}) elif length or perimeter: procedure_fmt = '%(geo_col)s' if geodetic and length: # There's no `length_sphere` procedure_fmt += ',%(spheroid)s' procedure_args.update({'function' : SpatialBackend.length_spheroid, 'spheroid' : where[1]}) # Setting up the settings for `_spatial_attribute`. 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: # The geometry is passed in as a parameter because we handled # transformation conditions in this routine. s['select_params'] = [SpatialBackend.Adaptor(geom)] return self._spatial_attribute(func, s, **kwargs)
def _distance_attribute(self, func, geom=None, tolerance=0.05, spheroid=False, **kwargs): """ DRY routine for GeoQuerySet distance attribute routines. """ # Setting up the distance procedure arguments. procedure_args, geo_field = self._spatial_setup(func, field_name=kwargs.get('field_name')) # If geodetic defaulting distance attribute to meters (Oracle and # PostGIS spherical distances return meters). Otherwise, use the # units of the geometry field. 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)) # Shortcut booleans for what distance function we're using and # whether the geometry field is 3D. 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 # The field's get_db_prep_lookup() is used to get any # extra distance parameters. Here we set up the # parameters that will be passed in to field's function. lookup_params = [geom or 'POINT (0 0)', 0] # Getting the spatial backend operations. backend = connection.ops # If the spheroid calculation is desired, either by the `spheroid` # keyword or when calculating the length of geodetic field, make # sure the 'spheroid' distance setting string is passed in so we # get the correct spatial stored procedure. 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) # The `geom_args` flag is set to true if a geometry parameter was # passed in. 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: # Getting whether this field is in units of degrees since the field may have # been transformed via the `transform` GeoQuerySet method. srid = self.query.get_context('transformed_srid') if srid: u, unit_name, s = get_srid_info(srid, connection) geodetic = unit_name.lower() in geo_field.geodetic_units if geodetic and not connection.features.supports_distance_geodetic: raise ValueError( 'This database does not support linear distance ' 'calculations on geodetic coordinate systems.' ) if distance: if srid: # Setting the `geom_args` flag to false because we want to handle # transformation SQL here, rather than the way done by default # (which will transform to the original SRID of the field rather # than to what was transformed to). geom_args = False procedure_fmt = '%s(%%(geo_col)s, %s)' % (backend.transform, srid) if geom.srid is None or geom.srid == srid: # If the geom parameter srid is None, it is assumed the coordinates # are in the transformed units. A placeholder is used for the # geometry parameter. `GeomFromText` constructor is also needed # to wrap geom placeholder for SpatiaLite. if backend.spatialite: procedure_fmt += ', %s(%%%%s, %s)' % (backend.from_text, srid) else: procedure_fmt += ', %%s' else: # We need to transform the geom to the srid specified in `transform()`, # so wrapping the geometry placeholder in transformation SQL. # SpatiaLite also needs geometry placeholder wrapped in `GeomFromText` # constructor. if backend.spatialite: procedure_fmt += (', %s(%s(%%%%s, %s), %s)' % ( backend.transform, backend.from_text, geom.srid, srid)) else: procedure_fmt += ', %s(%%%%s, %s)' % (backend.transform, srid) else: # `transform()` was not used on this GeoQuerySet. procedure_fmt = '%(geo_col)s,%(geom)s' if not geography and geodetic: # Spherical distance calculation is needed (because the geographic # field is geodetic). However, the PostGIS ST_distance_sphere/spheroid() # procedures may only do queries from point columns to point geometries # some error checking is required. if not backend.geography: if not isinstance(geo_field, PointField): raise ValueError('Spherical distance calculation only supported on PointFields.') if not str(Geometry(six.memoryview(params[0].ewkb)).geom_type) == 'Point': raise ValueError( 'Spherical distance calculation only supported with ' 'Point Geometry parameters' ) # The `function` procedure argument needs to be set differently for # geodetic distance calculations. if spheroid: # Call to distance_spheroid() requires spheroid param as well. 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: # There's no `length_sphere`, and `length_spheroid` also # works on 3D geometries. procedure_fmt += ",'%(spheroid)s'" procedure_args.update({'function': backend.length_spheroid, 'spheroid': params[1]}) elif geom_3d and connection.features.supports_3d_functions: # Use 3D variants of perimeter and length routines on supported backends. if perimeter: procedure_args.update({'function': backend.perimeter3d}) elif length: procedure_args.update({'function': backend.length3d}) # Setting up the settings for `_spatial_attribute`. 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: # The geometry is passed in as a parameter because we handled # transformation conditions in this routine. s['select_params'] = [backend.Adapter(geom)] return self._spatial_attribute(func, s, **kwargs)
import warnings