init backend presensi
This commit is contained in:
52
app/Modules/Geo/Entities/Zone.php
Normal file
52
app/Modules/Geo/Entities/Zone.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Geo\Entities;
|
||||
|
||||
use CodeIgniter\Entity\Entity;
|
||||
|
||||
/**
|
||||
* Zone Entity
|
||||
*
|
||||
* Represents a geographic zone/geofence in the system.
|
||||
*/
|
||||
class Zone extends Entity
|
||||
{
|
||||
/**
|
||||
* Attributes that can be mass assigned
|
||||
*
|
||||
* @var array<string>
|
||||
*/
|
||||
protected $allowedFields = [
|
||||
'zone_code',
|
||||
'zone_name',
|
||||
'latitude',
|
||||
'longitude',
|
||||
'radius_meters',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
/**
|
||||
* Attributes that should be cast to specific types
|
||||
*
|
||||
* @var array<string, string>
|
||||
*/
|
||||
protected $casts = [
|
||||
'id' => 'integer',
|
||||
'latitude' => 'float',
|
||||
'longitude' => 'float',
|
||||
'radius_meters' => 'integer',
|
||||
'is_active' => 'boolean',
|
||||
'created_at' => 'datetime',
|
||||
'updated_at' => 'datetime',
|
||||
];
|
||||
|
||||
/**
|
||||
* Check if zone is active
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function isActive(): bool
|
||||
{
|
||||
return (bool) $this->attributes['is_active'];
|
||||
}
|
||||
}
|
||||
95
app/Modules/Geo/Models/ZoneModel.php
Normal file
95
app/Modules/Geo/Models/ZoneModel.php
Normal file
@@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Geo\Models;
|
||||
|
||||
use App\Modules\Geo\Entities\Zone;
|
||||
use CodeIgniter\Model;
|
||||
|
||||
/**
|
||||
* Zone Model
|
||||
*
|
||||
* Handles database operations for zones.
|
||||
*/
|
||||
class ZoneModel extends Model
|
||||
{
|
||||
protected $table = 'zones';
|
||||
protected $primaryKey = 'id';
|
||||
protected $useAutoIncrement = true;
|
||||
protected $returnType = Zone::class;
|
||||
protected $useSoftDeletes = false;
|
||||
protected $protectFields = true;
|
||||
protected $allowedFields = [
|
||||
'zone_code',
|
||||
'zone_name',
|
||||
'latitude',
|
||||
'longitude',
|
||||
'radius_meters',
|
||||
'is_active',
|
||||
];
|
||||
|
||||
// Dates
|
||||
protected $useTimestamps = true;
|
||||
protected $dateFormat = 'datetime';
|
||||
protected $createdField = 'created_at';
|
||||
protected $updatedField = 'updated_at';
|
||||
protected $deletedField = 'deleted_at';
|
||||
|
||||
// Validation
|
||||
protected $validationRules = [
|
||||
'zone_code' => 'required|max_length[100]|is_unique[zones.zone_code,id,{id}]',
|
||||
'zone_name' => 'required|max_length[255]',
|
||||
'latitude' => 'required|decimal',
|
||||
'longitude' => 'required|decimal',
|
||||
'radius_meters' => 'required|integer|greater_than[0]',
|
||||
'is_active' => 'permit_empty|in_list[0,1]',
|
||||
];
|
||||
|
||||
protected $validationMessages = [];
|
||||
protected $skipValidation = false;
|
||||
protected $cleanValidationRules = true;
|
||||
|
||||
// Callbacks
|
||||
protected $allowCallbacks = true;
|
||||
protected $beforeInsert = [];
|
||||
protected $afterInsert = [];
|
||||
protected $beforeUpdate = [];
|
||||
protected $afterUpdate = [];
|
||||
protected $beforeFind = [];
|
||||
protected $afterFind = [];
|
||||
protected $beforeDelete = [];
|
||||
protected $afterDelete = [];
|
||||
|
||||
/**
|
||||
* Find zone by zone_code
|
||||
*
|
||||
* @param string $zoneCode
|
||||
* @return Zone|null
|
||||
*/
|
||||
public function findByZoneCode(string $zoneCode): ?Zone
|
||||
{
|
||||
return $this->where('zone_code', $zoneCode)->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find active zone by zone_code
|
||||
*
|
||||
* @param string $zoneCode
|
||||
* @return Zone|null
|
||||
*/
|
||||
public function findActiveByZoneCode(string $zoneCode): ?Zone
|
||||
{
|
||||
return $this->where('zone_code', $zoneCode)
|
||||
->where('is_active', 1)
|
||||
->first();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all active zones
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function findAllActive(): array
|
||||
{
|
||||
return $this->where('is_active', 1)->findAll();
|
||||
}
|
||||
}
|
||||
12
app/Modules/Geo/Routes.php
Normal file
12
app/Modules/Geo/Routes.php
Normal file
@@ -0,0 +1,12 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* Geo Module Routes
|
||||
*
|
||||
* This file is automatically loaded by ModuleLoader.
|
||||
* Define your geo/zone management routes here.
|
||||
*
|
||||
* @var RouteCollection $routes
|
||||
*/
|
||||
|
||||
// Geo routes will be defined here
|
||||
96
app/Modules/Geo/Services/GeoFenceService.php
Normal file
96
app/Modules/Geo/Services/GeoFenceService.php
Normal file
@@ -0,0 +1,96 @@
|
||||
<?php
|
||||
|
||||
namespace App\Modules\Geo\Services;
|
||||
|
||||
/**
|
||||
* Geo Fence Service
|
||||
*
|
||||
* Handles geofencing logic using Haversine formula for distance calculation.
|
||||
*/
|
||||
class GeoFenceService
|
||||
{
|
||||
/**
|
||||
* Earth's radius in meters
|
||||
*/
|
||||
private const EARTH_RADIUS_METERS = 6371000;
|
||||
|
||||
/**
|
||||
* Check if a point (latitude, longitude) is inside a zone
|
||||
*
|
||||
* Uses Haversine formula to calculate distance between two points
|
||||
* on Earth's surface and compares it to zone radius.
|
||||
*
|
||||
* @param float $lat Latitude of the point to check
|
||||
* @param float $lng Longitude of the point to check
|
||||
* @param array $zone Zone data with 'latitude', 'longitude', and 'radius_meters'
|
||||
* @return bool True if point is inside zone, false otherwise
|
||||
*/
|
||||
public function isInsideZone(float $lat, float $lng, array $zone): bool
|
||||
{
|
||||
// Validate zone data
|
||||
if (!isset($zone['latitude']) || !isset($zone['longitude']) || !isset($zone['radius_meters'])) {
|
||||
return false;
|
||||
}
|
||||
|
||||
$zoneLat = (float) $zone['latitude'];
|
||||
$zoneLng = (float) $zone['longitude'];
|
||||
$radiusMeters = (int) $zone['radius_meters'];
|
||||
|
||||
// Calculate distance using Haversine formula
|
||||
$distance = $this->calculateHaversineDistance($lat, $lng, $zoneLat, $zoneLng);
|
||||
|
||||
// Check if distance is within radius
|
||||
return $distance <= $radiusMeters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate distance between two points using Haversine formula
|
||||
*
|
||||
* Haversine formula calculates the great-circle distance between
|
||||
* two points on a sphere given their longitudes and latitudes.
|
||||
*
|
||||
* @param float $lat1 Latitude of first point
|
||||
* @param float $lng1 Longitude of first point
|
||||
* @param float $lat2 Latitude of second point
|
||||
* @param float $lng2 Longitude of second point
|
||||
* @return float Distance in meters
|
||||
*/
|
||||
private function calculateHaversineDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
|
||||
{
|
||||
// Convert degrees to radians
|
||||
$lat1Rad = deg2rad($lat1);
|
||||
$lng1Rad = deg2rad($lng1);
|
||||
$lat2Rad = deg2rad($lat2);
|
||||
$lng2Rad = deg2rad($lng2);
|
||||
|
||||
// Calculate differences
|
||||
$deltaLat = $lat2Rad - $lat1Rad;
|
||||
$deltaLng = $lng2Rad - $lng1Rad;
|
||||
|
||||
// Haversine formula
|
||||
$a = sin($deltaLat / 2) * sin($deltaLat / 2) +
|
||||
cos($lat1Rad) * cos($lat2Rad) *
|
||||
sin($deltaLng / 2) * sin($deltaLng / 2);
|
||||
|
||||
$c = 2 * atan2(sqrt($a), sqrt(1 - $a));
|
||||
|
||||
// Distance in meters
|
||||
$distance = self::EARTH_RADIUS_METERS * $c;
|
||||
|
||||
return $distance;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculate distance between two points (public helper method)
|
||||
*
|
||||
* @param float $lat1 Latitude of first point
|
||||
* @param float $lng1 Longitude of first point
|
||||
* @param float $lat2 Latitude of second point
|
||||
* @param float $lng2 Longitude of second point
|
||||
* @return float Distance in meters
|
||||
*/
|
||||
public function calculateDistance(float $lat1, float $lng1, float $lat2, float $lng2): float
|
||||
{
|
||||
return $this->calculateHaversineDistance($lat1, $lng1, $lat2, $lng2);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user