Use GPS to send correct item locations to server, support multiple location types, close #6
This commit is contained in:
parent
f81c43d053
commit
e7f1a9caa0
@ -35,6 +35,16 @@ $("#addpackagebtn").click(function () {
|
||||
});
|
||||
return;
|
||||
}
|
||||
if ($("input[name=zipcode]").val().trim() == "") {
|
||||
playSound("error");
|
||||
app.toast.show({
|
||||
text: "Please fill in a ZIP code.",
|
||||
position: "bottom",
|
||||
destroyOnClose: true,
|
||||
closeTimeout: 1000 * 10
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// Save city/state if changed
|
||||
if (getStorage("citystate") != $("input[name=citystate]").val().trim()) {
|
||||
@ -49,6 +59,7 @@ $("#addpackagebtn").click(function () {
|
||||
$("#no-history").addClass("display-none");
|
||||
addPackageByAddress(
|
||||
$("input[name=number]").val().toUpperCase(),
|
||||
$("input[name=unit]").val().toUpperCase(),
|
||||
$("input[name=street]").val().toUpperCase(),
|
||||
$("input[name=citystate]").val().toUpperCase(),
|
||||
$("input[name=zipcode]").val().toUpperCase(),
|
||||
@ -68,6 +79,9 @@ $("#addpackagebtn").click(function () {
|
||||
+ ' </div>'
|
||||
+ '</li>');
|
||||
});
|
||||
// Clear out unit value since it's unlikely to
|
||||
// be used twice
|
||||
$("input[name=unit]").val("");
|
||||
});
|
||||
|
||||
// Remove any pre-existing click handlers from the history list,
|
||||
|
@ -11,7 +11,8 @@ var userPosition = {
|
||||
latitude: 0.0,
|
||||
longitude: 0.0,
|
||||
accuracy: 999999
|
||||
}
|
||||
},
|
||||
updated: 0
|
||||
};
|
||||
|
||||
// Preload last known location while GPS warms up
|
||||
@ -31,6 +32,7 @@ $.ajax({
|
||||
userPosition.coords.latitude = resp.location.latitude;
|
||||
userPosition.coords.longitude = resp.location.longitude;
|
||||
userPosition.coords.accuracy = 99999;
|
||||
userPosition.updated = time();
|
||||
}
|
||||
}
|
||||
});
|
||||
@ -41,7 +43,8 @@ var mapLocationControlStarted = false;
|
||||
|
||||
if ("geolocation" in navigator) {
|
||||
navigator.geolocation.watchPosition(function (position) {
|
||||
userPosition = position;
|
||||
userPosition.coords = position.coords;
|
||||
userPosition.updated = time();
|
||||
setStorage("user_latitude", userPosition.coords.latitude);
|
||||
setStorage("user_longitude", userPosition.coords.longitude);
|
||||
if (mapLocationControlStarted) {
|
||||
|
@ -91,6 +91,13 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
|
||||
deadline = false;
|
||||
}
|
||||
|
||||
if (typeof address == "object") {
|
||||
var extendedaddress = address;
|
||||
address = extendedaddress.address;
|
||||
} else {
|
||||
var extendedaddress = false;
|
||||
}
|
||||
|
||||
// Extra precision makes the map stupider,
|
||||
// and doesn't really increase accuracy since four decimal places ~= 11m
|
||||
latitude = +(parseFloat("" + latitude).toFixed(4));
|
||||
@ -103,6 +110,7 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
|
||||
if (packages[i].coords[0] == latitude && packages[i].coords[1] == longitude) {
|
||||
coordsID = packages[i].id;
|
||||
packages[i].items.push({
|
||||
extended: extendedaddress,
|
||||
address: address,
|
||||
delivered: false,
|
||||
type: type,
|
||||
@ -124,6 +132,7 @@ function addPackage(address, latitude, longitude, type, callback, deadline) {
|
||||
address: address,
|
||||
items: [
|
||||
{
|
||||
extended: extendedaddress,
|
||||
address: address,
|
||||
delivered: false,
|
||||
type: type,
|
||||
@ -203,10 +212,96 @@ function importPackageList(newlist) {
|
||||
return skipped;
|
||||
}
|
||||
|
||||
function mapCalibrate(item, packagesentry) {
|
||||
// Determine if the delivery location isn't near the map pin
|
||||
if (userPosition.coords.accuracy < 20 && timeDiff(userPosition.updated) < 10) {
|
||||
// User location is accurate, check distance
|
||||
var distance = getDistance(packagesentry.coords[0], packagesentry.coords[1], userPosition.coords.latitude, userPosition.coords.longitude);
|
||||
var lat = userPosition.coords.latitude;
|
||||
var lon = userPosition.coords.longitude;
|
||||
if (distance > 100) { // Over 100 meters distance
|
||||
if (typeof item.extended == "object") {
|
||||
// we have all the info we need
|
||||
var fixmap = function (item, latitude, longitude, locationtype) {
|
||||
$.ajax({
|
||||
type: "POST",
|
||||
url: SETTINGS.mapfixapi,
|
||||
data: {
|
||||
number: item.extended.number,
|
||||
unit: item.extended.unit,
|
||||
street: item.extended.street,
|
||||
citystate: item.extended.citystate,
|
||||
zip: item.extended.zip,
|
||||
latitude: latitude,
|
||||
longitude: longitude,
|
||||
locationtype: locationtype
|
||||
},
|
||||
success: function () {
|
||||
app.toast.show({
|
||||
text: "Calibration recorded. Thank you for improving the map!",
|
||||
position: "bottom",
|
||||
destroyOnClose: true,
|
||||
closeTimeout: 1000 * 3
|
||||
});
|
||||
},
|
||||
error: function () {
|
||||
// try again in five minutes
|
||||
setTimeout(function () {
|
||||
fixmap(item, latitude, longitude, locationtype);
|
||||
}, 1000 * 60 * 5);
|
||||
},
|
||||
dataType: "json"
|
||||
});
|
||||
};
|
||||
|
||||
app.dialog.create({
|
||||
title: 'Map Calibration',
|
||||
text: "Your actual location doesn't match the map location for the " + SETTINGS.itemtypes[item.type].name + " at " + item.address + ". Where are you?",
|
||||
buttons: [
|
||||
{
|
||||
text: 'Address',
|
||||
close: true
|
||||
},
|
||||
{
|
||||
text: 'Mailbox/CBU',
|
||||
close: true
|
||||
},
|
||||
{
|
||||
text: 'Parcel Locker',
|
||||
close: true
|
||||
},
|
||||
{
|
||||
text: "Other/Cancel",
|
||||
close: true
|
||||
}
|
||||
],
|
||||
verticalButtons: true,
|
||||
onClick: function (dialog, index) {
|
||||
switch (index) {
|
||||
case 0:
|
||||
fixmap(item, lat, lon, "address");
|
||||
break;
|
||||
case 1:
|
||||
fixmap(item, lat, lon, "mailbox");
|
||||
break;
|
||||
case 2:
|
||||
fixmap(item, lat, lon, "locker");
|
||||
break;
|
||||
default:
|
||||
return;
|
||||
}
|
||||
}
|
||||
}).open();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function markDelivered(id, delivered) {
|
||||
for (var i = 0; i < packages.length; i++) {
|
||||
for (var j = 0; j < packages[i].items.length; j++) {
|
||||
if (packages[i].items[j].id == id) {
|
||||
|
||||
if (typeof delivered == 'undefined') {
|
||||
if (packages[i].items[j].delivered == false) {
|
||||
delivered = true;
|
||||
@ -218,12 +313,16 @@ function markDelivered(id, delivered) {
|
||||
packages[i].items[j].delivered = delivered;
|
||||
if (delivered) {
|
||||
packages[i].items[j].deliverytimestamp = Date.now();
|
||||
|
||||
setStorage("packages", JSON.stringify(packages));
|
||||
|
||||
mapCalibrate(packages[i].items[j], packages[i]);
|
||||
|
||||
return; // so we don't keep looping over the rest of the packages
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
setStorage("packages", JSON.stringify(packages));
|
||||
}
|
||||
|
||||
function confirmDeletePackage(package, callback) {
|
||||
@ -284,7 +383,7 @@ function countPackages() {
|
||||
return count;
|
||||
}
|
||||
|
||||
function addPackageByAddress(number, street, citystate, zip, type, callback) {
|
||||
function addPackageByAddress(number, unit, street, citystate, zip, type, callback) {
|
||||
var requestfinished = false;
|
||||
var searchingdialogopen = false;
|
||||
var deadline = false;
|
||||
@ -297,7 +396,10 @@ function addPackageByAddress(number, street, citystate, zip, type, callback) {
|
||||
}
|
||||
|
||||
geocodecache = JSON.parse(geocodecache);
|
||||
var cachekey = number + " || " + street + " || " + citystate;
|
||||
var cachekey = number + " || " + street + " || " + citystate + " || " + zip + " || " + SETTINGS.itemtypes[type].allowedlocationtypes;
|
||||
if (unit != '') {
|
||||
cachekey = number + " || " + unit + " || " + street + " || " + citystate + " || " + zip + " || " + SETTINGS.itemtypes[type].allowedlocationtypes;
|
||||
}
|
||||
var cacheitem = geocodecache[cachekey];
|
||||
var timestamp = Math.floor(Date.now() / 1000);
|
||||
if (typeof cacheitem != 'undefined') {
|
||||
@ -307,7 +409,14 @@ function addPackageByAddress(number, street, citystate, zip, type, callback) {
|
||||
setStorage("geocode_cache", JSON.stringify(geocodecache));
|
||||
} else {
|
||||
console.log("Info", "Using cached geocode result", cacheitem);
|
||||
addPackage(cacheitem.address, cacheitem.latitude, cacheitem.longitude, type, callback, deadline);
|
||||
addPackage({
|
||||
address: cacheitem.address,
|
||||
number: number,
|
||||
unit: unit,
|
||||
street: street,
|
||||
citystate: citystate,
|
||||
zip: zip
|
||||
}, cacheitem.latitude, cacheitem.longitude, type, callback, deadline);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -317,9 +426,11 @@ function addPackageByAddress(number, street, citystate, zip, type, callback) {
|
||||
dataType: 'json',
|
||||
data: {
|
||||
number: number,
|
||||
unit: unit,
|
||||
street: street,
|
||||
citystate: citystate,
|
||||
zip: zip
|
||||
zip: zip,
|
||||
type: SETTINGS.itemtypes[type].allowedlocationtypes
|
||||
},
|
||||
timeout: 15 * 1000,
|
||||
success: function (resp) {
|
||||
@ -330,8 +441,17 @@ function addPackageByAddress(number, street, citystate, zip, type, callback) {
|
||||
requestfinished = true;
|
||||
if (resp.status == "OK") {
|
||||
if (resp.accuracy.ok) {
|
||||
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
|
||||
addPackage({
|
||||
address: resp.address.street,
|
||||
number: number,
|
||||
unit: unit,
|
||||
street: street,
|
||||
citystate: citystate,
|
||||
zip: zip
|
||||
}, resp.coords[0], resp.coords[1], type, callback, deadline);
|
||||
geocodecache[cachekey] = {
|
||||
number: resp.address.number,
|
||||
unit: unit,
|
||||
address: resp.address.street,
|
||||
latitude: resp.coords[0],
|
||||
longitude: resp.coords[1],
|
||||
@ -345,9 +465,23 @@ function addPackageByAddress(number, street, citystate, zip, type, callback) {
|
||||
"Accuracy Warning",
|
||||
function (ok) {
|
||||
if (resp.address.street == "") {
|
||||
addPackage(address, resp.coords[0], resp.coords[1], type, callback, deadline);
|
||||
addPackage({
|
||||
address: address,
|
||||
number: number,
|
||||
unit: unit,
|
||||
street: street,
|
||||
citystate: citystate,
|
||||
zip: zip
|
||||
}, resp.coords[0], resp.coords[1], type, callback, deadline);
|
||||
} else {
|
||||
addPackage(resp.address.street, resp.coords[0], resp.coords[1], type, callback, deadline);
|
||||
addPackage({
|
||||
address: resp.address.street,
|
||||
number: number,
|
||||
unit: unit,
|
||||
street: street,
|
||||
citystate: citystate,
|
||||
zip: zip
|
||||
}, resp.coords[0], resp.coords[1], type, callback, deadline);
|
||||
}
|
||||
}
|
||||
);
|
||||
|
@ -39,3 +39,20 @@ function timestampToTimeString(timestamp) {
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the current UNIX timestamp in seconds.
|
||||
* @returns {Number}
|
||||
*/
|
||||
function time() {
|
||||
return Date.now() / 1000;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the number of seconds between now and the given timestamp.
|
||||
* @param {Number} compareto
|
||||
* @returns {Number}
|
||||
*/
|
||||
function timeDiff(compareto) {
|
||||
return time() - compareto;
|
||||
}
|
@ -37,12 +37,23 @@
|
||||
<div class="list no-margin-top">
|
||||
<ul>
|
||||
<li class="item-divider">Address</li>
|
||||
<li class="item-content item-input">
|
||||
<div class="item-inner">
|
||||
<div class="item-title item-label">Address Number</div>
|
||||
<div class="item-input-wrap">
|
||||
<input type="number" name="number" placeholder="1234" autocomplete="off" autocorrect="off" autocapitalize="off">
|
||||
<span class="input-clear-button"></span>
|
||||
<li>
|
||||
<div class="row justify-content-stretch">
|
||||
<div class="col-70 item-content item-input">
|
||||
<div class="item-inner">
|
||||
<div class="item-title item-label">Number</div>
|
||||
<div class="item-input-wrap">
|
||||
<input type="number" name="number" placeholder="1234" value="" autocomplete="off" autocorrect="off">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-30 item-content item-input no-padding-left">
|
||||
<div class="item-inner no-padding-right">
|
||||
<div class="item-title item-label">Unit</div>
|
||||
<div class="item-input-wrap">
|
||||
<input type="text" name="unit" placeholder="" value="" autocomplete="off" autocorrect="off">
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
@ -57,7 +68,7 @@
|
||||
</li>
|
||||
<li>
|
||||
<div class="row justify-content-stretch">
|
||||
<div class="col-75 item-content item-input">
|
||||
<div class="col-70 item-content item-input">
|
||||
<div class="item-inner">
|
||||
<div class="item-title item-label">City, State</div>
|
||||
<div class="item-input-wrap">
|
||||
@ -65,7 +76,7 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="col-25 item-content item-input no-padding-left">
|
||||
<div class="col-30 item-content item-input no-padding-left">
|
||||
<div class="item-inner no-padding-right">
|
||||
<div class="item-title item-label">ZIP</div>
|
||||
<div class="item-input-wrap">
|
||||
|
@ -50,6 +50,15 @@ var SETTINGS = {
|
||||
file: "alert.sonar.mp3"
|
||||
}
|
||||
},
|
||||
/*
|
||||
* id = key: internal type ID
|
||||
* name: human-readable type label
|
||||
* icon: CSS classes for displaying an icon with <i class="[icon]"></i>
|
||||
* mapicon: assets/images/[mapicon].png
|
||||
* pluralmapicon: same as mapicon but when there's more than one of the type
|
||||
* selected: true for the default type, not present for other entries
|
||||
* allowedlocationtypes: Pipe-seperated list of location types this item can be delivered to. Should include "address" somewhere to keep the server happy.
|
||||
*/
|
||||
itemtypes: {
|
||||
package: {
|
||||
id: "package",
|
||||
@ -57,42 +66,48 @@ var SETTINGS = {
|
||||
icon: "fas fa-box fa-fw",
|
||||
mapicon: "box",
|
||||
pluralmapicon: "boxes",
|
||||
selected: true
|
||||
selected: true,
|
||||
allowedlocationtypes: "locker|address"
|
||||
},
|
||||
large: {
|
||||
id: "large",
|
||||
name: "Large Package",
|
||||
icon: "fas fa-pallet fa-fw",
|
||||
mapicon: "largebox",
|
||||
pluralmapicon: "largeboxes"
|
||||
pluralmapicon: "largeboxes",
|
||||
allowedlocationtypes: "address"
|
||||
},
|
||||
small: {
|
||||
id: "small",
|
||||
name: "Small Package",
|
||||
icon: "fas fa-envelope-square fa-fw",
|
||||
mapicon: "smallpackage",
|
||||
pluralmapicon: "smallpackages"
|
||||
pluralmapicon: "smallpackages",
|
||||
allowedlocationtypes: "mailbox|locker|address"
|
||||
},
|
||||
letter: {
|
||||
id: "letter",
|
||||
name: "Letter",
|
||||
icon: "fas fa-envelope fa-fw",
|
||||
mapicon: "envelope",
|
||||
pluralmapicon: "envelopes"
|
||||
pluralmapicon: "envelopes",
|
||||
allowedlocationtypes: "mailbox|address"
|
||||
},
|
||||
signature: {
|
||||
id: "signature",
|
||||
name: "Signature Item",
|
||||
icon: "fas fa-file-signature fa-fw",
|
||||
mapicon: "signature",
|
||||
pluralmapicon: "signatures"
|
||||
pluralmapicon: "signatures",
|
||||
allowedlocationtypes: "address"
|
||||
},
|
||||
express: {
|
||||
id: "express",
|
||||
name: "Express Item",
|
||||
icon: "fas fa-shipping-fast fa-fw",
|
||||
mapicon: "express",
|
||||
pluralmapicon: "multiple-items"
|
||||
pluralmapicon: "multiple-items",
|
||||
allowedlocationtypes: "address"
|
||||
}
|
||||
},
|
||||
weathericons: [
|
||||
@ -334,5 +349,6 @@ var SETTINGS = {
|
||||
geoipapi: "https://apis.netsyms.net/packagehelper/geoip.php",
|
||||
sharelistapi: "https://apis.netsyms.net/packagehelper/sharepackagelist.php",
|
||||
loginurl: "https://apis.netsyms.net/packagehelper/login/",
|
||||
syncapi: "https://apis.netsyms.net/packagehelper/sync.php"
|
||||
syncapi: "https://apis.netsyms.net/packagehelper/sync.php",
|
||||
mapfixapi: "https://apis.netsyms.net/packagehelper/contribgeocode.php",
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user