How Geofencing Zones Work
This article provides an in-depth technical explanation of how zones function in Levy Fleets, covering the enforcement pipeline, ride-end validation, event logging, and customer notifications.
Architecture Overview
Levy Fleets uses zones in two primary contexts:
1. Live Telemetry Enforcement
When a vehicle sends GPS coordinates (latitude/longitude) during an active ride, the system:
- Loads all speed-limit and no-go zones for the vehicle's subaccount
- Evaluates whether the vehicle's location falls within any zone polygons
- Sends IoT commands to adjust speed or disable the motor as needed
- Logs zone entry/exit events
- Sends customer notifications
2. Ride-End Validation
When a customer attempts to end a ride, the system:
- Loads all parking, no-parking, and no-go zones
- Evaluates whether the ending location satisfies parking requirements
- Blocks or allows the ride end based on zone membership
Real-Time Processing
Zone enforcement runs on every GPS telemetry update from vehicles during active rides. This ensures immediate response to zone entries and exits.
Zone Data Structure
Zones are stored with the following key properties:
| Property | Type | Description |
|---|---|---|
id | UUID | Unique identifier |
subaccount_id | UUID | The subaccount (location) this zone belongs to |
name | TEXT | Human-readable zone name |
type | ENUM | parking, no_parking, no_go, speed_limit, charging, bonus, service_area |
geojson | JSONB | GeoJSON Feature with Polygon geometry |
speed_limit_kph | INTEGER | Speed limit (1-80 km/h, only for speed_limit type) |
parking_reward_points | INTEGER | Points awarded for parking here (bonus zones) |
is_preferred_parking | BOOLEAN | Whether this is a preferred parking location |
rebalancing_multiplier | DECIMAL | Points multiplier for rebalancing incentives |
GeoJSON Format
Zone boundaries are stored as GeoJSON Feature objects:
{
"type": "Feature",
"properties": {},
"geometry": {
"type": "Polygon",
"coordinates": [
[
[-122.4194, 37.7749],
[-122.4180, 37.7749],
[-122.4180, 37.7735],
[-122.4194, 37.7735],
[-122.4194, 37.7749]
]
]
}
}
Coordinate Format
Coordinates are in [longitude, latitude] format (GeoJSON standard), not [latitude, longitude]. This is a common source of errors when importing zone data.
Real-Time Enforcement Flow
The enforcement pipeline runs on every GPS telemetry update from vehicles during active rides.
Step 1: Trigger Conditions
Enforcement is triggered when:
- A GPS update is received from a vehicle
- The vehicle has an active ride (
ride_status = 'in_progress') - The vehicle has a valid IoT IMEI configured
- Valid coordinates are provided (finite latitude and longitude)
Step 2: Zone Loading
The system queries all speed_limit and no_go zones for the vehicle's subaccount that are not deleted.
Step 3: Point-in-Polygon Evaluation
For each zone, the system uses a ray-casting algorithm to determine if the vehicle's location is inside the polygon:
- Cast a ray from the point to infinity
- Count how many times the ray crosses polygon edges
- Odd count = inside, Even count = outside
The system evaluates the vehicle's current position against all loaded zones and categorizes them:
- Speed zones: All speed_limit zones containing the vehicle
- No-go zones: All no_go zones containing the vehicle
Step 4: Determine Active Restrictions
For overlapping speed zones:
- The system calculates the effective speed limit for each zone
- If the zone has a
speed_limit_kphvalue, it uses that - Otherwise, it falls back to the subaccount's
slow_speed_zone_limit_kphsetting - The lowest limit among all overlapping zones is applied
For no-go zones:
- Any no-go zone containing the vehicle triggers motor shutdown
- Only one no-go zone needs to match for enforcement
Step 5: IoT Command Dispatch
Based on the evaluation, IoT commands are sent to the vehicle to enforce speed limits or disable the motor.
Step 6: Command Deduplication
To prevent unnecessary IoT traffic, commands are deduplicated:
- If the vehicle is already in the same zone with the same speed limit, no command is sent
- The vehicle's current zone state is tracked:
current_speed_limit_zone_idcurrent_speed_limit_kphcurrent_no_go_zone_idengine_disabled_by_zone
Step 7: State Updates
After successful command dispatch, the vehicle record is updated with the new zone state and enforcement timestamp.
Ride-End Validation
When a customer attempts to end a ride, the system validates their location against zone rules.
Validation Flow
- Load all zones (not filtered by type)
- Get location data:
devicePoint: Customer's phone GPS coordinates (from request body)vehiclePoint: Vehicle's last known GPS coordinates (from vehicles table)
- Determine validation mode from subaccount settings
- Evaluate zone membership for each relevant point
- Return result (allow or block with error code)
Validation Rules
For each point being validated:
- Check if point is inside ANY parking zone (
type = 'parking') - Check if point is inside ANY no-go zone (
type = 'no_go') - Check if point is inside ANY no-parking zone (
type = 'no_parking')
Result Formula:
valid = inParking && !inNoGo && !inNoParking
Zone Priority
When zones overlap, the following priority applies:
- No-go zones - Always block, regardless of other zones
- No-parking zones - Block parking even if inside a parking zone
- Parking zones - Only valid if not in a no-go or no-parking zone
Parking Zone Validation Modes
Subaccount settings control how parking validation works. Configure this in Settings -> Other -> Parking Zone Validation Mode.
Vehicle Only (Default)
- Only the vehicle's GPS location is checked
- Customer's phone location is ignored
- Falls back to phone location if vehicle location unavailable
Use case: Most reliable for vehicle-centric operations where phone GPS may be inaccurate indoors.
Both
- Both vehicle AND customer phone must be in a valid parking zone
- If either is missing, the request is rejected with
location_data_missing - Most restrictive mode
Use case: High-security environments where you want to ensure the customer is physically with the vehicle.
Hybrid
- Either vehicle OR customer phone can be in a valid parking zone
- Most permissive mode
- Passes if any available point satisfies the requirement
Use case: Areas with poor GPS coverage or when flexibility is preferred.
Zone Event Logging
Every zone entry and exit during active rides is logged for audit and analytics purposes.
Event Data Captured
| Field | Description |
|---|---|
ride_uuid | The ride during which the event occurred |
zone_id | The zone that was entered/exited |
zone_type | Type of zone (speed_limit, no_go, parking) |
event_type | enter or exit |
occurred_at | Timestamp of the event |
vehicle_latitude | Vehicle location at event time |
vehicle_longitude | Vehicle location at event time |
enforcement_action | Action taken (speed_limited, engine_disabled, engine_enabled, speed_restored) |
speed_limit_applied | Speed limit applied (for speed zones) |
previous_speed_limit | Previous speed limit (for transitions) |
iot_command_sent | Actual IoT command string sent |
iot_command_success | Whether the command was acknowledged |
Viewing Events
Zone events can be viewed:
- In the ride detail page (Dashboard -> Rides -> [ride] -> Zones tab)
- Via database queries for analytics
- Through the Partner API for external integrations
Customer Notifications
Customers receive push notifications when entering or exiting zones with enforcement.
Speed Zone Notifications
Entering:
Title: "Slow zone activated"
Body: "Your vehicle is now limited to 10 km/h in Downtown Pedestrian Zone."
Exiting:
Title: "Slow zone cleared"
Body: "You have exited Downtown Pedestrian Zone. Your top speed is restored."
No-Go Zone Notifications
Entering:
Title: "No-go zone entered"
Body: "Ride controls paused in Private Property Area. Move to an approved area to continue."
Exiting:
Title: "No-go zone cleared"
Body: "You have left Private Property Area. Ride controls are restored."
Notification Deduplication
To prevent notification spam:
- A 60-second cooldown applies per zone/event combination
- Duplicate notifications within this window are suppressed
- Notifications are logged for audit
GPS Accuracy Considerations
GPS accuracy varies based on environment, affecting zone behavior.
Typical Accuracy Ranges
| Environment | GPS Accuracy |
|---|---|
| Open sky | 3-5 meters |
| Urban canyon | 10-30 meters |
| Near buildings | 5-15 meters |
| Indoor/covered | 20-50+ meters or no signal |
Hysteresis Protection
To prevent "flickering" at zone boundaries due to GPS noise, the system implements hysteresis:
No-Go Zone Exit Hysteresis:
- Vehicle must stream GPS points outside the zone for at least 15 seconds before motor power is restored
- Prevents false exits from a single GPS reading that drifts outside
- Tracked via
no_go_enforced_attimestamp on the vehicle
GPS Buffer
When designing zones, leave 5-10 meter margins at critical boundaries to account for GPS accuracy variations.
Design Recommendations
- Buffer zones: Leave 5-10 meter margins at critical boundaries
- Polygon simplicity: Simpler shapes are less affected by GPS jitter
- Zone sizing: Very small zones (< 20m radius) may not enforce reliably
- Testing: Validate zone boundaries with actual devices in the field
Caching and Performance
Speed Limit Caching
The subaccount's slow_speed_zone_limit_kph setting is cached for 60 seconds:
- Reduces database queries during high-frequency telemetry processing
- Cache is per-subaccount
- Changes to settings take up to 60 seconds to propagate
Vehicle Model Speed Caching
Vehicle model maximum speed is cached for 5 minutes:
- Used when restoring speed after exiting speed zones
- Cache is per-vehicle-model-id
Zone Loading
Zones are loaded fresh on each enforcement call (not cached):
- Ensures zone changes take effect immediately
- Queries are optimized with indexes on subaccount_id, type, and deleted_at
Performance Tips
- Limit zone count: Fewer zones = faster evaluation
- Simple polygons: Fewer vertices = faster point-in-polygon checks
- Avoid overlapping zones: Reduces the number of evaluations needed
Scenario Playbook
Scenario 1: Ending a Ride with Parking Validation
Setup:
- Validation mode:
both - A parking zone covers the street
- A no-parking zone covers a loading dock within the parking zone
Action: Customer tries to end ride at the loading dock
Result:
- Device point: inside parking zone, inside no-parking zone
- Vehicle point: inside parking zone, inside no-parking zone
- Validation:
inParking=true,inNoParking=true - Result:
valid = true && !true = false - API returns
not_in_parking_zoneerror - Customer must relocate vehicle
Scenario 2: Speed Zone Transition
Setup:
- Zone A: speed_limit = 12 km/h
- Zone B: speed_limit = 8 km/h
- Subaccount slow_speed_zone_limit_kph = 10 km/h
Action: Vehicle moves from outside zones -> Zone A -> Zone A+B overlap -> Zone B -> outside
Result:
- Outside -> Zone A: Speed command sent with limit = min(12, 10) = 10 km/h
- Zone A -> A+B: Speed command sent with limit = min(8, 10) = 8 km/h
- A+B -> Zone B: No change (still 8 km/h, deduplicated)
- Zone B -> Outside: Speed restored to vehicle model max (e.g., 25 km/h)
Scenario 3: Overlapping Speed and No-Go Zones
Setup:
- Speed zone covering a park (10 km/h)
- No-go zone covering a construction site within the park
Action: Vehicle enters the construction site
Result:
- Speed limit applied: 10 km/h (from speed zone)
- Motor disabled (from no-go zone)
- Both events logged
- Customer receives no-go zone notification
- Vehicle must leave the no-go zone AND wait 15 seconds before motor is restored
- Speed limit remains at 10 km/h until vehicle also exits the speed zone
Scenario 4: GPS Drift at Zone Boundary
Setup:
- No-go zone boundary
- Vehicle is physically at the edge
- GPS readings fluctuate inside/outside
Action: GPS readings alternate: inside -> outside -> inside -> outside (rapidly)
Result:
- First "inside" reading: Motor disabled, timer starts
- First "outside" reading: Hysteresis check - timer not expired, motor stays disabled
- Second "inside" reading: Still in zone, motor stays disabled
- Second "outside" reading: Hysteresis check - if >15s outside, motor restored
The 15-second hysteresis prevents the motor from rapidly toggling on/off due to GPS noise.
Service Area and Out-of-Zone Alerts
Service Area zones define your fleet's operational boundary and trigger alerts when vehicles leave this area.
Configuring Out-of-Zone Alerts
Enable the feature:
- Go to Dashboard -> Settings
- Navigate to the Other section
- Enable Out-of-Zone Alert
Configure email recipients:
- Go to Settings -> Alerts
- Enable Out-of-Zone Alerts under Operational Alerts
- Set the Critical Alerts Email address
Alert Trigger Conditions
An out-of-zone alert is sent when:
- A vehicle has an active ride
- The vehicle's GPS location is outside all service_area zones for the subaccount
- The
out_of_zone_alert_enabledsetting is true - The
out_of_zone_alertsemail notification is enabled
Alert Content
The email includes:
- Vehicle number
- Last known location
- Time of detection
- Link to view the ride in the dashboard
Buffer Zone Configuration
The buffer zone setting adds a GPS accuracy margin around zone boundaries.
Purpose
GPS signals can vary by 5-15 meters depending on environment. The buffer zone setting helps account for this variance by:
- Adding margin around zone boundaries
- Reducing false positives at zone edges
- Improving enforcement reliability
Configuration
Set the buffer zone in Settings -> Other:
- Default: 10 meters
- Recommended range: 5-15 meters
| GPS Environment | Recommended Buffer |
|---|---|
| Open areas | 5 meters |
| Urban/downtown | 10-15 meters |
| Near tall buildings | 15+ meters |
| Indoor/covered | Consider disabling enforcement |
Mobile App Zone Display
Zones are displayed on the customer mobile app map with specific colors and behaviors.
Zone Visibility
| Zone Type | Visible on App | Color |
|---|---|---|
| Parking | Yes | Green (semi-transparent) |
| No-Parking | Yes | Yellow/Orange (semi-transparent) |
| No-Go | Yes | Red (semi-transparent) |
| Speed Limit | Yes | Yellow (same as no-parking) |
| Charging | No | - |
| Bonus | No | - |
| Service Area | No | - |
Mobile App Behavior
Zone Loading:
- Zones are fetched from the mobile API endpoint
- Cached for 5 minutes to reduce API calls
- Refreshed when app comes to foreground
Zone Rendering:
- Displayed as colored polygon overlays on the map
- Stroke (border) color is more saturated than fill
- Stroke width: 2 pixels
Invalid Coordinate Handling:
- The mobile app validates zone coordinates before rendering
- Checks for valid arrays with at least 3 coordinates
- Validates each point is a number and finite
- Silently skips zones with invalid geometry
Zone Analytics and Reporting
Levy Fleets provides comprehensive analytics for zone interactions.
Zone Statistics
Zone statistics provide aggregated data:
| Metric | Description |
|---|---|
total_entries | Number of zone entry events |
total_exits | Number of zone exit events |
unique_rides | Distinct rides that entered this zone |
avg_duration_seconds | Average time spent in zone |
total_enforcement_actions | Count of IoT commands sent |
Dashboard Analytics
Zone analytics are visible in several dashboard locations:
-
Ride Detail Page -> Zones Tab
- Chronological list of zone events
- Entry/exit times
- Enforcement actions taken
- Duration in each zone
-
Zone Management Page
- Zone usage counts (when expanded)
- Last activity timestamp
Exporting Zone Data
Zone event data can be exported via:
- Direct database queries for advanced analysis
- Ride CSV exports (include zone events when available)
- Partner API for integration with external BI tools
What's Next?
- Creating and Managing Zones - How to create and configure zones
- Speed Limit Zones - Deep dive into speed limit enforcement
- Dashboard Overview - Complete dashboard guide
Technical Mastery
Understanding how zones work under the hood helps you design more effective geofencing strategies and troubleshoot issues when they arise. Use this knowledge to optimize your zone configuration for your specific operational needs.