Geospatial Operators โ
Location-based filtering with powerful spatial operators for proximity search, bounding boxes, and polygon containment.
Interactive Playground ๐ฎ โ
Try geospatial operators live with real Buenos Aires data! Click the map, adjust controls, and see real-time filtering.
๐ Filtered Results
Generated Filter Code
filter(restaurants, {
"location": {
"$near": {
"center": {
"lat": -34.577645,
"lng": -58.42672
},
"maxDistanceMeters": 2000
}
}
})๐ก This filter expression will return 91 items
Geospatial Operator
Proximity Settings
๐ก Click the map to change center point
Creates an exclusion zone around the center
Additional Filters
Overview โ
Geospatial operators allow you to filter data based on geographic coordinates. Perfect for:
- Restaurant and store locators
- Real estate property searches
- Delivery zone validation
- IoT device monitoring
- Location-based services
Available Operators โ
$near- Find points within radius$geoBox- Find points within bounding box$geoPolygon- Find points within polygon
GeoPoint Type โ
All geospatial operators use the GeoPoint interface:
interface GeoPoint {
lat: number; // Latitude: -90 to 90
lng: number; // Longitude: -180 to 180
}$near - Proximity Search โ
Find points within a specified radius of a center point.
Basic Usage โ
import { filter, type GeoPoint } from '@mcabreradev/filter';
interface Restaurant {
name: string;
location: GeoPoint;
rating: number;
}
const userLocation: GeoPoint = { lat: 52.52, lng: 13.405 };
filter(restaurants, {
location: {
$near: {
center: userLocation,
maxDistanceMeters: 2000
}
}
});With Minimum Distance โ
Exclude points that are too close:
filter(restaurants, {
location: {
$near: {
center: userLocation,
maxDistanceMeters: 5000,
minDistanceMeters: 1000
}
}
});Distance Calculation โ
Uses the spherical law of cosines for fast, accurate distance calculation:
- Suitable for most use cases
- Earth radius: 6,371,000 meters
- Returns distance in meters
- Handles edge cases (poles, date line)
$geoBox - Bounding Box โ
Find points within a rectangular area.
Basic Usage โ
filter(stores, {
location: {
$geoBox: {
southwest: { lat: 52.5, lng: 13.3 },
northeast: { lat: 52.6, lng: 13.5 }
}
}
});Use Cases โ
Delivery zone:
const deliveryZone = {
southwest: { lat: 40.7, lng: -74.02 },
northeast: { lat: 40.8, lng: -73.9 }
};
filter(orders, {
deliveryAddress: {
$geoBox: deliveryZone
},
status: 'pending'
});Map viewport:
const getVisibleMarkers = (bounds: BoundingBox) => {
return filter(markers, {
location: { $geoBox: bounds }
});
};$geoPolygon - Polygon Containment โ
Find points inside a custom polygon area.
Basic Usage โ
const neighborhoodBoundary = {
points: [
{ lat: 52.51, lng: 13.4 },
{ lat: 52.54, lng: 13.4 },
{ lat: 52.54, lng: 13.43 },
{ lat: 52.51, lng: 13.43 }
]
};
filter(properties, {
location: {
$geoPolygon: neighborhoodBoundary
}
});Complex Polygons โ
Supports any polygon shape:
const schoolDistrict = {
points: [
{ lat: 52.5, lng: 13.3 },
{ lat: 52.55, lng: 13.35 },
{ lat: 52.6, lng: 13.3 },
{ lat: 52.6, lng: 13.5 },
{ lat: 52.5, lng: 13.5 }
]
};
filter(schools, {
location: {
$geoPolygon: schoolDistrict
}
});Requirements โ
- Minimum 3 points required
- Points define the polygon boundary
- Uses ray casting algorithm
- Automatically closes polygon
Combining with Other Operators โ
With Comparison Operators โ
filter(restaurants, {
location: {
$near: {
center: userLocation,
maxDistanceMeters: 3000
}
},
rating: { $gte: 4.5 },
priceLevel: { $lte: 2 }
});With Logical Operators โ
filter(restaurants, {
$and: [
{
location: {
$near: {
center: userLocation,
maxDistanceMeters: 5000
}
}
},
{
$or: [
{ cuisine: 'Italian' },
{ cuisine: 'Japanese' }
]
},
{
isOpen: true,
rating: { $gte: 4.0 }
}
]
});With String Operators โ
filter(restaurants, {
location: {
$geoBox: deliveryZone
},
name: { $contains: 'pizza' },
tags: { $contains: 'delivery' }
});Utility Functions โ
Calculate Distance โ
import { calculateDistance } from '@mcabreradev/filter';
const berlin: GeoPoint = { lat: 52.52, lng: 13.405 };
const paris: GeoPoint = { lat: 48.8566, lng: 2.3522 };
const distance = calculateDistance(berlin, paris);
console.log(`${(distance / 1000).toFixed(0)} km`);Validate Coordinates โ
import { isValidGeoPoint } from '@mcabreradev/filter';
isValidGeoPoint({ lat: 52.52, lng: 13.405 });
isValidGeoPoint({ lat: 91, lng: 0 });
isValidGeoPoint({ lat: 0, lng: 181 });Evaluate Operators Directly โ
import { evaluateNear, evaluateGeoBox, evaluateGeoPolygon } from '@mcabreradev/filter';
const point: GeoPoint = { lat: 52.52, lng: 13.405 };
evaluateNear(point, {
center: { lat: 52.52, lng: 13.4 },
maxDistanceMeters: 1000
});
evaluateGeoBox(point, {
southwest: { lat: 52.5, lng: 13.3 },
northeast: { lat: 52.6, lng: 13.5 }
});
evaluateGeoPolygon(point, {
points: [
{ lat: 52.5, lng: 13.4 },
{ lat: 52.6, lng: 13.4 },
{ lat: 52.6, lng: 13.5 }
]
});Real-World Examples โ
Restaurant Finder โ
interface Restaurant {
name: string;
location: GeoPoint;
cuisine: string;
rating: number;
priceLevel: number;
isOpen: boolean;
}
const findRestaurants = (
userLoc: GeoPoint,
options: {
maxDistance?: number;
cuisine?: string;
minRating?: number;
maxPrice?: number;
} = {}
) => {
const {
maxDistance = 5000,
cuisine,
minRating = 4.0,
maxPrice = 4
} = options;
return filter(restaurants, {
location: {
$near: {
center: userLoc,
maxDistanceMeters: maxDistance
}
},
...(cuisine && { cuisine }),
rating: { $gte: minRating },
priceLevel: { $lte: maxPrice },
isOpen: true
});
};
const nearbyItalian = findRestaurants(
{ lat: 52.52, lng: 13.405 },
{ cuisine: 'Italian', maxDistance: 3000 }
);Delivery Zone Validator โ
const deliveryZones = [
{
name: 'Downtown',
boundary: {
southwest: { lat: 40.7, lng: -74.02 },
northeast: { lat: 40.75, lng: -73.95 }
}
},
{
name: 'Midtown',
boundary: {
southwest: { lat: 40.75, lng: -74.0 },
northeast: { lat: 40.8, lng: -73.92 }
}
}
];
const isDeliveryAvailable = (address: GeoPoint): boolean => {
return deliveryZones.some(zone => {
const result = filter([{ location: address }], {
location: { $geoBox: zone.boundary }
});
return result.length > 0;
});
};Property Search โ
interface Property {
address: string;
location: GeoPoint;
price: number;
bedrooms: number;
sqft: number;
features: string[];
}
const searchProperties = (
criteria: {
neighborhood?: GeoPoint[];
maxPrice?: number;
minBedrooms?: number;
requiredFeatures?: string[];
}
) => {
const {
neighborhood,
maxPrice = 1000000,
minBedrooms = 1,
requiredFeatures = []
} = criteria;
return filter(properties, {
...(neighborhood && {
location: {
$geoPolygon: { points: neighborhood }
}
}),
price: { $lte: maxPrice },
bedrooms: { $gte: minBedrooms },
...(requiredFeatures.length > 0 && {
features: {
$in: requiredFeatures
}
})
});
};IoT Device Monitoring โ
interface Device {
id: string;
location: GeoPoint;
status: 'online' | 'offline' | 'maintenance';
batteryLevel: number;
lastSeen: Date;
}
const getDevicesInArea = (
center: GeoPoint,
radiusMeters: number,
options?: {
status?: string;
minBattery?: number;
}
) => {
return filter(devices, {
location: {
$near: {
center,
maxDistanceMeters: radiusMeters
}
},
...(options?.status && { status: options.status }),
...(options?.minBattery && {
batteryLevel: { $gte: options.minBattery }
})
});
};
const criticalDevices = getDevicesInArea(
{ lat: 52.52, lng: 13.405 },
10000,
{ status: 'online', minBattery: 20 }
);Performance Optimization โ
Use Lazy Evaluation โ
For large datasets, use lazy evaluation:
import { filterLazy, filterFirst } from '@mcabreradev/filter';
const nearbyLazy = filterLazy(millionRestaurants, {
location: {
$near: {
center: userLocation,
maxDistanceMeters: 5000
}
}
});
for (const restaurant of nearbyLazy) {
if (found === 10) break;
}
const first20 = filterFirst(millionRestaurants, {
location: {
$near: {
center: userLocation,
maxDistanceMeters: 3000
}
}
}, 20);Choose the Right Operator โ
- Bounding box is fastest for rectangular areas
- Proximity is best for "nearby" searches
- Polygon is most flexible but slightly slower
Enable Caching โ
For repeated queries:
filter(restaurants, {
location: {
$near: {
center: userLocation,
maxDistanceMeters: 5000
}
}
}, { enableCache: true });Edge Cases โ
Invalid Coordinates โ
Invalid coordinates are automatically excluded:
const points = [
{ location: { lat: 52.52, lng: 13.405 } },
{ location: { lat: 91, lng: 0 } },
{ location: { lat: 0, lng: 181 } }
];
filter(points, {
location: {
$near: {
center: { lat: 52.52, lng: 13.405 },
maxDistanceMeters: 1000
}
}
});Missing Location Data โ
Items without location are excluded:
const items = [
{ name: 'A', location: { lat: 52.52, lng: 13.405 } },
{ name: 'B' },
{ name: 'C', location: { lat: 52.521, lng: 13.406 } }
];
filter(items, {
location: {
$near: {
center: { lat: 52.52, lng: 13.405 },
maxDistanceMeters: 1000
}
}
});Polygon Requirements โ
Polygons must have at least 3 points:
evaluateGeoPolygon(point, {
points: [
{ lat: 52.5, lng: 13.4 }
]
});
evaluateGeoPolygon(point, {
points: [
{ lat: 52.5, lng: 13.4 },
{ lat: 52.6, lng: 13.4 },
{ lat: 52.6, lng: 13.5 }
]
});TypeScript Support โ
Full type safety with intelligent autocomplete:
import type {
GeoPoint,
NearQuery,
BoundingBox,
PolygonQuery,
GeospatialOperators
} from '@mcabreradev/filter';
const nearQuery: NearQuery = {
center: { lat: 52.52, lng: 13.405 },
maxDistanceMeters: 5000,
minDistanceMeters: 500
};
const box: BoundingBox = {
southwest: { lat: 52.5, lng: 13.3 },
northeast: { lat: 52.6, lng: 13.5 }
};
const polygon: PolygonQuery = {
points: [
{ lat: 52.5, lng: 13.4 },
{ lat: 52.6, lng: 13.4 },
{ lat: 52.6, lng: 13.5 }
]
};Distance Calculation Details โ
The library uses the spherical law of cosines for distance calculation:
distance = R ร arccos(sin(ฯ1) ร sin(ฯ2) + cos(ฯ1) ร cos(ฯ2) ร cos(ฮฮป))Where:
R= Earth radius (6,371,000 meters)ฯ1, ฯ2= latitudes in radiansฮฮป= longitude difference in radians
This formula provides:
- Fast computation
- Accuracy suitable for most applications
- Handles edge cases correctly
Coordinate System โ
All coordinates use the WGS84 standard:
- Latitude range: -90ยฐ (South Pole) to 90ยฐ (North Pole)
- Longitude range: -180ยฐ to 180ยฐ
- Positive latitude = North
- Positive longitude = East