fix office location detection radius and GPS accuracy
This commit is contained in:
@@ -251,6 +251,8 @@ SVG;
|
|||||||
|
|
||||||
const referenceLat = 32.6504751;
|
const referenceLat = 32.6504751;
|
||||||
const referenceLng = 51.6936045;
|
const referenceLng = 51.6936045;
|
||||||
|
const OFFICE_RADIUS_METERS = 100;
|
||||||
|
const MAX_ACCURACY_CREDIT_METERS = 80;
|
||||||
|
|
||||||
const submitBtn = document.getElementById("submitBtn");
|
const submitBtn = document.getElementById("submitBtn");
|
||||||
const latInput = document.getElementById("latitude");
|
const latInput = document.getElementById("latitude");
|
||||||
@@ -278,26 +280,31 @@ SVG;
|
|||||||
return earthRadius * c;
|
return earthRadius * c;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInsideOffice(distanceMeters, accuracyMeters) {
|
||||||
|
const accuracyCredit = Math.min(Math.max(0, accuracyMeters || 0), MAX_ACCURACY_CREDIT_METERS);
|
||||||
|
return Math.max(0, distanceMeters - accuracyCredit) <= OFFICE_RADIUS_METERS;
|
||||||
|
}
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition(
|
navigator.geolocation.getCurrentPosition(
|
||||||
position => {
|
position => {
|
||||||
const lat = position.coords.latitude;
|
const lat = position.coords.latitude;
|
||||||
const lng = position.coords.longitude;
|
const lng = position.coords.longitude;
|
||||||
|
const accuracy = position.coords.accuracy || 0;
|
||||||
|
|
||||||
latInput.value = lat;
|
latInput.value = lat;
|
||||||
lngInput.value = lng;
|
lngInput.value = lng;
|
||||||
|
|
||||||
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
|
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
|
||||||
distanceInput.value = dist;
|
distanceInput.value = dist;
|
||||||
if (dist < 20) {
|
const inside = isInsideOffice(dist, accuracy);
|
||||||
distanceMessage.textContent = `مکان: شرکت`;
|
distanceMessage.textContent = inside
|
||||||
} else {
|
? `مکان: شرکت (~${dist}م)`
|
||||||
distanceMessage.textContent = `مکان: خارج از شرکت`;
|
: `مکان: خارج از شرکت (~${dist}م)`;
|
||||||
}
|
|
||||||
|
|
||||||
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
|
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed", "text-green-500", "text-blue-500", "text-orange-500", "text-gray-400");
|
||||||
if (dist < 10) {
|
if (inside) {
|
||||||
distanceMessage.classList.add("text-green-500");
|
distanceMessage.classList.add("text-green-500");
|
||||||
} else if (dist < 50) {
|
} else if (dist < OFFICE_RADIUS_METERS * 2) {
|
||||||
distanceMessage.classList.add("text-blue-500");
|
distanceMessage.classList.add("text-blue-500");
|
||||||
} else if (dist > 2000) {
|
} else if (dist > 2000) {
|
||||||
distanceMessage.classList.add("text-orange-500");
|
distanceMessage.classList.add("text-orange-500");
|
||||||
@@ -311,7 +318,7 @@ SVG;
|
|||||||
},
|
},
|
||||||
error => alert("⛔ خطا در دریافت موقعیت مکانی: " + error.message), {
|
error => alert("⛔ خطا در دریافت موقعیت مکانی: " + error.message), {
|
||||||
enableHighAccuracy: true,
|
enableHighAccuracy: true,
|
||||||
timeout: 10000,
|
timeout: 20000,
|
||||||
maximumAge: 0
|
maximumAge: 0
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -311,12 +311,12 @@ if (!empty($user->tasks)) {
|
|||||||
</td>
|
</td>
|
||||||
|
|
||||||
{{-- زمان ورود --}}
|
{{-- زمان ورود --}}
|
||||||
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->entered_at && $item->distance ? ($item->requested !== null ? '#fad1d1' :($item->distance < 50 ? '#d1fae5' : '#edae80')) : '' }};">
|
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->entered_at && $item->distance ? ($item->requested !== null ? '#fad1d1' :($item->distance < 100 ? '#d1fae5' : '#edae80')) : '' }};">
|
||||||
{{ $item->entered_at ? \Carbon\Carbon::parse($item->entered_at)->format('H:i') : '-' }}
|
{{ $item->entered_at ? \Carbon\Carbon::parse($item->entered_at)->format('H:i') : '-' }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
{{-- زمان خروج --}}
|
{{-- زمان خروج --}}
|
||||||
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->exited_at && $item->distance_exit ? ($item->requested !== null ? '#fad1d1' :($item->distance_exit < 50 ? '#d1fae5' : '#edae80')) : '' }};">
|
<td style="border-right: 1px solid #ddd; padding: 4px; background-color: {{$item->exited_at && $item->distance_exit ? ($item->requested !== null ? '#fad1d1' :($item->distance_exit < 100 ? '#d1fae5' : '#edae80')) : '' }};">
|
||||||
{{ $item->exited_at ? \Carbon\Carbon::parse($item->exited_at)->format('H:i') : '-' }}
|
{{ $item->exited_at ? \Carbon\Carbon::parse($item->exited_at)->format('H:i') : '-' }}
|
||||||
</td>
|
</td>
|
||||||
|
|
||||||
@@ -646,8 +646,12 @@ if (!empty($user->tasks)) {
|
|||||||
const distanceMessage = document.getElementById("distanceMessage");
|
const distanceMessage = document.getElementById("distanceMessage");
|
||||||
|
|
||||||
|
|
||||||
|
// مختصات دفتر / شرکت
|
||||||
const referenceLat = 32.6504751;
|
const referenceLat = 32.6504751;
|
||||||
const referenceLng = 51.6936045;
|
const referenceLng = 51.6936045;
|
||||||
|
// شعاع مجاز (متر) — GPS داخل ساختمان معمولاً ۲۰–۸۰ متر خطا دارد
|
||||||
|
const OFFICE_RADIUS_METERS = 100;
|
||||||
|
const MAX_ACCURACY_CREDIT_METERS = 80;
|
||||||
|
|
||||||
const submitBtn = document.getElementById("submitBtn");
|
const submitBtn = document.getElementById("submitBtn");
|
||||||
const latInput = document.getElementById("latitude");
|
const latInput = document.getElementById("latitude");
|
||||||
@@ -677,57 +681,136 @@ if (!empty($user->tasks)) {
|
|||||||
return earthRadius * c;
|
return earthRadius * c;
|
||||||
}
|
}
|
||||||
|
|
||||||
navigator.geolocation.getCurrentPosition(
|
function isInsideOffice(distanceMeters, accuracyMeters) {
|
||||||
position => {
|
const accuracyCredit = Math.min(Math.max(0, accuracyMeters || 0), MAX_ACCURACY_CREDIT_METERS);
|
||||||
const lat = position.coords.latitude;
|
const effectiveDistance = Math.max(0, distanceMeters - accuracyCredit);
|
||||||
const lng = position.coords.longitude;
|
return effectiveDistance <= OFFICE_RADIUS_METERS;
|
||||||
|
}
|
||||||
|
|
||||||
latInput.value = lat;
|
function applyLocationResult(lat, lng, accuracyMeters) {
|
||||||
lngInput.value = lng;
|
latInput.value = lat;
|
||||||
|
lngInput.value = lng;
|
||||||
|
|
||||||
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
|
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
|
||||||
distanceInput.value = dist;
|
distanceInput.value = dist;
|
||||||
if (dist < 20) {
|
const inside = isInsideOffice(dist, accuracyMeters);
|
||||||
distanceMessage.textContent = `مکان: شرکت`;
|
const accuracyText = accuracyMeters
|
||||||
} else {
|
? ` · دقت ±${Math.round(accuracyMeters)}م`
|
||||||
distanceMessage.textContent = `مکان: خارج از شرکت`;
|
: '';
|
||||||
}
|
|
||||||
|
|
||||||
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
|
distanceMessage.textContent = inside
|
||||||
if (dist < 10) {
|
? `مکان: شرکت (~${dist}م)${accuracyText}`
|
||||||
distanceMessage.classList.add("text-green-500");
|
: `مکان: خارج از شرکت (~${dist}م)${accuracyText}`;
|
||||||
} else if (dist < 50) {
|
|
||||||
distanceMessage.classList.add("text-blue-500");
|
|
||||||
} else if (dist > 2000) {
|
|
||||||
distanceMessage.classList.add("text-orange-500");
|
|
||||||
} else {
|
|
||||||
distanceMessage.classList.add("text-gray-400");
|
|
||||||
}
|
|
||||||
submitBtn.disabled = false;
|
|
||||||
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
|
|
||||||
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
|
|
||||||
|
|
||||||
},
|
distanceMessage.classList.remove(
|
||||||
error => {
|
"bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500",
|
||||||
const lat = 32.6520532791536; // از مختصات ثابت استفاده کنید
|
"cursor-not-allowed", "text-green-500", "text-blue-500",
|
||||||
const lng = 51.68472614422439; // از مختصات ثابت استفاده کنید
|
"text-orange-500", "text-gray-400"
|
||||||
|
);
|
||||||
|
if (inside) {
|
||||||
|
distanceMessage.classList.add("text-green-500");
|
||||||
|
} else if (dist <= OFFICE_RADIUS_METERS * 2) {
|
||||||
|
distanceMessage.classList.add("text-blue-500");
|
||||||
|
} else if (dist > 2000) {
|
||||||
|
distanceMessage.classList.add("text-orange-500");
|
||||||
|
} else {
|
||||||
|
distanceMessage.classList.add("text-gray-400");
|
||||||
|
}
|
||||||
|
|
||||||
const dist = Math.round(getDistanceInMeters(referenceLat, referenceLng, lat, lng));
|
submitBtn.disabled = false;
|
||||||
distanceInput.value = dist;
|
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
|
||||||
distanceMessage.textContent = `مکان: خارج از شرکت`;
|
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
|
||||||
|
}
|
||||||
|
|
||||||
distanceMessage.classList.remove("bg-gray-400", "bg-blue-500", "bg-green-500", "bg-orange-500", "cursor-not-allowed");
|
function locateUser() {
|
||||||
|
if (!navigator.geolocation) {
|
||||||
|
distanceMessage.textContent = 'موقعیتیاب در دسترس نیست';
|
||||||
distanceMessage.classList.add("text-orange-500");
|
distanceMessage.classList.add("text-orange-500");
|
||||||
submitBtn.disabled = false;
|
submitBtn.disabled = false;
|
||||||
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
|
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
|
||||||
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
|
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
|
||||||
|
return;
|
||||||
// alert("⛔ خطا در دریافت موقعیت مکانی: " + error.message), {
|
|
||||||
// enableHighAccuracy: true,
|
|
||||||
// timeout: 10000,
|
|
||||||
// maximumAge: 0
|
|
||||||
}
|
}
|
||||||
);
|
|
||||||
|
distanceMessage.textContent = 'در حال بررسی موقعیت...';
|
||||||
|
distanceMessage.classList.remove("text-green-500", "text-blue-500", "text-orange-500", "text-gray-400");
|
||||||
|
|
||||||
|
const geoOptions = {
|
||||||
|
enableHighAccuracy: true,
|
||||||
|
timeout: 20000,
|
||||||
|
maximumAge: 0,
|
||||||
|
};
|
||||||
|
|
||||||
|
let bestReading = null;
|
||||||
|
let watchId = null;
|
||||||
|
let finished = false;
|
||||||
|
|
||||||
|
const finishWithBest = () => {
|
||||||
|
if (finished) return;
|
||||||
|
finished = true;
|
||||||
|
if (watchId !== null) {
|
||||||
|
navigator.geolocation.clearWatch(watchId);
|
||||||
|
}
|
||||||
|
if (bestReading) {
|
||||||
|
applyLocationResult(
|
||||||
|
bestReading.lat,
|
||||||
|
bestReading.lng,
|
||||||
|
bestReading.accuracy
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const considerReading = (position) => {
|
||||||
|
const lat = position.coords.latitude;
|
||||||
|
const lng = position.coords.longitude;
|
||||||
|
const accuracy = position.coords.accuracy || 9999;
|
||||||
|
const dist = getDistanceInMeters(referenceLat, referenceLng, lat, lng);
|
||||||
|
const score = dist + Math.min(accuracy, 150);
|
||||||
|
|
||||||
|
if (!bestReading || score < bestReading.score) {
|
||||||
|
bestReading = { lat, lng, accuracy, dist, score };
|
||||||
|
}
|
||||||
|
|
||||||
|
// اگر با دقت خوب داخل محدوده شدیم، زودتر تمام کن
|
||||||
|
if (isInsideOffice(dist, accuracy) && accuracy <= 60) {
|
||||||
|
finishWithBest();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
(position) => {
|
||||||
|
considerReading(position);
|
||||||
|
// چند ثانیه watch برای خواندن دقیقتر داخل ساختمان
|
||||||
|
watchId = navigator.geolocation.watchPosition(
|
||||||
|
considerReading,
|
||||||
|
() => finishWithBest(),
|
||||||
|
geoOptions
|
||||||
|
);
|
||||||
|
setTimeout(finishWithBest, 5000);
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
// تلاش دوم بدون الزام دقت بالا
|
||||||
|
navigator.geolocation.getCurrentPosition(
|
||||||
|
(position) => {
|
||||||
|
considerReading(position);
|
||||||
|
finishWithBest();
|
||||||
|
},
|
||||||
|
() => {
|
||||||
|
distanceMessage.textContent = 'موقعیت دریافت نشد — میتوانید ورود بزنید';
|
||||||
|
distanceMessage.classList.add("text-orange-500");
|
||||||
|
distanceInput.value = '';
|
||||||
|
submitBtn.disabled = false;
|
||||||
|
submitBtn.classList.remove("bg-gray-400", "cursor-not-allowed");
|
||||||
|
submitBtn.classList.add("bg-cyan-500", "hover:bg-cyan-700", "cursor-pointer");
|
||||||
|
},
|
||||||
|
{ enableHighAccuracy: false, timeout: 10000, maximumAge: 60000 }
|
||||||
|
);
|
||||||
|
},
|
||||||
|
geoOptions
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
locateUser();
|
||||||
const attendanceForm = document.getElementById('attendanceForm');
|
const attendanceForm = document.getElementById('attendanceForm');
|
||||||
|
|
||||||
const output = document.getElementById('output');
|
const output = document.getElementById('output');
|
||||||
|
|||||||
Reference in New Issue
Block a user