Spot v2: Project Management
This commit is contained in:
25
files/db/update_v1_to_v2.sql
Normal file
25
files/db/update_v1_to_v2.sql
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
CREATE TABLE `projects` (
|
||||||
|
`id_project` int(10) UNSIGNED auto_increment,
|
||||||
|
`codename` VARCHAR(100),
|
||||||
|
`name` VARCHAR(100),
|
||||||
|
`active_from` DATETIME,
|
||||||
|
`active_to` DATETIME,
|
||||||
|
`geofile` VARCHAR(50),
|
||||||
|
`timezone` VARCHAR(100),
|
||||||
|
`led` TIMESTAMP NOT NULL ON UPDATE CURRENT_TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||||||
|
PRIMARY KEY (`id_project`));
|
||||||
|
|
||||||
|
INSERT INTO projects (name, codename, active_from, active_to, geofile, timezone) VALUES ('Te Araroa', 'te_araroa', '2015-12-29 00:00:00', '2016-03-05 23:59:59', 'te_araroa.geojson', 'Pacific/Auckland');
|
||||||
|
INSERT INTO projects (name, codename, active_from, active_to, geofile, timezone) VALUES ('HRP', 'hrp', '2019-06-01 00:00:00', '2019-09-10 23:59:59', 'hrp.geojson', 'Europe/Paris');
|
||||||
|
|
||||||
|
ALTER TABLE feeds ADD COLUMN id_project int(10) UNSIGNED AFTER id_spot;
|
||||||
|
ALTER TABLE feeds ADD last_update DATETIME AFTER status;
|
||||||
|
ALTER TABLE feeds ADD FOREIGN KEY (`id_project`) REFERENCES projects(`id_project`);
|
||||||
|
UPDATE feeds SET last_update = led;
|
||||||
|
UPDATE feeds SET id_project = 1;
|
||||||
|
|
||||||
|
ALTER TABLE posts ADD COLUMN id_project int(10) UNSIGNED AFTER id_post;
|
||||||
|
ALTER TABLE posts ADD timestamp DATETIME AFTER content;
|
||||||
|
ALTER TABLE posts ADD FOREIGN KEY (`id_project`) REFERENCES projects(`id_project`);
|
||||||
|
UPDATE posts SET timestamp = led;
|
||||||
|
UPDATE posts SET id_project = 1;
|
||||||
36
geo/hrp.geojson
Normal file
36
geo/hrp.geojson
Normal file
File diff suppressed because one or more lines are too long
153
geo/te_araroa.geojson
Normal file
153
geo/te_araroa.geojson
Normal file
File diff suppressed because one or more lines are too long
BIN
images/layers.png
Normal file
BIN
images/layers.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 696 B |
BIN
images/marker-icon-2x.png
Normal file
BIN
images/marker-icon-2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.4 KiB |
BIN
images/marker-icon.png
Normal file
BIN
images/marker-icon.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.4 KiB |
BIN
images/marker-shadow.png
Normal file
BIN
images/marker-shadow.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 618 B |
212
inc/cacher.php
212
inc/cacher.php
@@ -1,103 +1,109 @@
|
|||||||
<?php
|
<?php
|
||||||
|
|
||||||
class Cacher extends PhpObject
|
class Cacher extends PhpObject
|
||||||
{
|
{
|
||||||
const CACHE_FOLDER = 'cache/';
|
const CACHE_FOLDER = 'cache/';
|
||||||
const DATA_RETENTION = 60 * 60 * 24 * 90; //3 months
|
const DATA_RETENTION = 60 * 60 * 24 * 90; //3 months
|
||||||
|
|
||||||
private $sPattern;
|
private $sPattern;
|
||||||
|
|
||||||
//Params
|
//Params
|
||||||
private $sId;
|
private $sId;
|
||||||
private $iX;
|
private $iX;
|
||||||
private $iY;
|
private $iY;
|
||||||
private $iZ;
|
private $iZ;
|
||||||
private $sToken;
|
private $sToken;
|
||||||
private $asDomains;
|
private $asDomains;
|
||||||
|
private $sReferer;
|
||||||
public function __construct($sPattern, $sId='')
|
|
||||||
{
|
public function __construct($sPattern, $sId='')
|
||||||
parent::__construct(__CLASS__, Settings::DEBUG);
|
{
|
||||||
$this->setPattern($sPattern);
|
parent::__construct(__CLASS__, Settings::DEBUG);
|
||||||
$this->setId($sId);
|
$this->setPattern($sPattern);
|
||||||
$this->iX = 0;
|
$this->setId($sId);
|
||||||
$this->iY = 0;
|
$this->iX = 0;
|
||||||
$this->iZ = 0;
|
$this->iY = 0;
|
||||||
$this->sToken = '';
|
$this->iZ = 0;
|
||||||
$this->asDomains = array();
|
$this->sToken = '';
|
||||||
}
|
$this->asDomains = array();
|
||||||
|
$this->sReferer = '';
|
||||||
public function setId($sId) {
|
}
|
||||||
$this->sId = $sId;
|
|
||||||
}
|
public function setId($sId) {
|
||||||
|
$this->sId = $sId;
|
||||||
public function setPattern($sPattern) {
|
}
|
||||||
$this->sPattern = $sPattern;
|
|
||||||
}
|
public function setPattern($sPattern) {
|
||||||
|
$this->sPattern = $sPattern;
|
||||||
public function setToken($sToken) {
|
}
|
||||||
$this->sToken = $sToken;
|
|
||||||
}
|
public function setToken($sToken) {
|
||||||
|
$this->sToken = $sToken;
|
||||||
public function setDomains($asDomains) {
|
}
|
||||||
$this->asDomains = $asDomains;
|
|
||||||
}
|
public function setDomains($asDomains) {
|
||||||
|
$this->asDomains = $asDomains;
|
||||||
public function setGeoPos($iX, $iY, $iZ) {
|
}
|
||||||
$this->iX = $iX;
|
|
||||||
$this->iY = $iY;
|
public function setReferer($sReferer) {
|
||||||
$this->iZ = $iZ;
|
$this->sReferer = $sReferer;
|
||||||
}
|
}
|
||||||
|
|
||||||
public function pushTile($iX, $iY, $iZ) {
|
public function setGeoPos($iX, $iY, $iZ) {
|
||||||
$this->setGeoPos($iX, $iY, $iZ);
|
$this->iX = $iX;
|
||||||
$sTilePath = $this->getFilePath();
|
$this->iY = $iY;
|
||||||
if(!$this->isFileAvailable($sTilePath)) {
|
$this->iZ = $iZ;
|
||||||
$oCurl = curl_init();
|
}
|
||||||
curl_setopt($oCurl, CURLOPT_URL, $this->getUrl());
|
|
||||||
curl_setopt($oCurl, CURLOPT_HEADER, false);
|
public function pushTile($iX, $iY, $iZ) {
|
||||||
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, true);
|
$this->setGeoPos($iX, $iY, $iZ);
|
||||||
curl_setopt($oCurl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
$sTilePath = $this->getFilePath();
|
||||||
curl_setopt($oCurl, CURLOPT_REFERER, 'http://spot.lutran.fr');
|
if(!$this->isFileAvailable($sTilePath)) {
|
||||||
$sContent = curl_exec($oCurl);
|
$oCurl = curl_init();
|
||||||
curl_close($oCurl);
|
curl_setopt($oCurl, CURLOPT_URL, $this->getUrl());
|
||||||
|
curl_setopt($oCurl, CURLOPT_HEADER, false);
|
||||||
file_put_contents($sTilePath, $sContent);
|
curl_setopt($oCurl, CURLOPT_RETURNTRANSFER, true);
|
||||||
}
|
curl_setopt($oCurl, CURLOPT_USERAGENT, $_SERVER['HTTP_USER_AGENT']);
|
||||||
else $sContent = file_get_contents($sTilePath);
|
curl_setopt($oCurl, CURLOPT_REFERER, $this->sReferer);
|
||||||
|
$sContent = curl_exec($oCurl);
|
||||||
header('Content-type: '.mime_content_type($sTilePath));
|
curl_close($oCurl);
|
||||||
return $sContent;
|
|
||||||
}
|
file_put_contents($sTilePath, $sContent);
|
||||||
|
}
|
||||||
public function getUrl() {
|
else $sContent = file_get_contents($sTilePath);
|
||||||
$asParams = $this->getUrlParams();
|
|
||||||
$sUrl = $this->sPattern;
|
header('Content-type: '.mime_content_type($sTilePath));
|
||||||
foreach($asParams as $sParam=>$sValue) {
|
return $sContent;
|
||||||
$sUrl = str_replace('{'.$sParam.'}', $sValue, $sUrl);
|
}
|
||||||
}
|
|
||||||
return $sUrl;
|
public function getUrl() {
|
||||||
}
|
$asParams = $this->getUrlParams();
|
||||||
|
$sUrl = $this->sPattern;
|
||||||
private function isFileAvailable($sTilePath) {
|
foreach($asParams as $sParam=>$sValue) {
|
||||||
if(!file_exists($sTilePath)) return false;
|
$sUrl = str_replace('{'.$sParam.'}', $sValue, $sUrl);
|
||||||
else return (time() - filemtime($sTilePath) < self::DATA_RETENTION);
|
}
|
||||||
}
|
return $sUrl;
|
||||||
|
}
|
||||||
private function getFilePath() {
|
|
||||||
$asParams = $this->getUrlParams();
|
private function isFileAvailable($sTilePath) {
|
||||||
return self::CACHE_FOLDER.'/'.$asParams['id'].'_'.$asParams['x'].'_'.$asParams['y'].'_'.$asParams['z'].'.tile';
|
if(!file_exists($sTilePath)) return false;
|
||||||
}
|
else return (time() - filemtime($sTilePath) < self::DATA_RETENTION);
|
||||||
|
}
|
||||||
private function getUrlParams() {
|
|
||||||
return array(
|
private function getFilePath() {
|
||||||
'id' => $this->sId,
|
$asParams = $this->getUrlParams();
|
||||||
'x' => $this->iX,
|
return self::CACHE_FOLDER.'/'.$asParams['id'].'_'.$asParams['x'].'_'.$asParams['y'].'_'.$asParams['z'].'.tile';
|
||||||
'y' => $this->iY,
|
}
|
||||||
'z' => $this->iZ,
|
|
||||||
'token' => $this->sToken,
|
private function getUrlParams() {
|
||||||
's' => empty($this->asDomains)?'':$this->asDomains[array_rand($this->asDomains)]
|
return array(
|
||||||
);
|
'id' => $this->sId,
|
||||||
}
|
'x' => $this->iX,
|
||||||
}
|
'y' => $this->iY,
|
||||||
|
'z' => $this->iZ,
|
||||||
|
'token' => $this->sToken,
|
||||||
|
's' => empty($this->asDomains)?'':$this->asDomains[array_rand($this->asDomains)]
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|||||||
111
inc/project.php
Normal file
111
inc/project.php
Normal file
@@ -0,0 +1,111 @@
|
|||||||
|
<?php
|
||||||
|
|
||||||
|
class Project extends PhpObject {
|
||||||
|
|
||||||
|
//Spot Mode
|
||||||
|
const MODE_PREVIZ = 'P';
|
||||||
|
const MODE_BLOG = 'B';
|
||||||
|
const MODE_HISTO = 'H';
|
||||||
|
const MODES = array('previz'=>self::MODE_PREVIZ, 'blog'=>self::MODE_BLOG, 'histo'=>self::MODE_HISTO);
|
||||||
|
|
||||||
|
//Folders
|
||||||
|
const GEO_FOLDER = 'geo/';
|
||||||
|
|
||||||
|
//DB Tables
|
||||||
|
const PROJ_TABLE = 'projects';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Database Handle
|
||||||
|
* @var Db
|
||||||
|
*/
|
||||||
|
private $oDb;
|
||||||
|
|
||||||
|
private $iProjectId;
|
||||||
|
private $sMode;
|
||||||
|
private $sName;
|
||||||
|
private $sCodeName;
|
||||||
|
private $asActive;
|
||||||
|
private $sGeo;
|
||||||
|
|
||||||
|
public function __construct(Db &$oDb) {
|
||||||
|
parent::__construct(__CLASS__, Settings::DEBUG);
|
||||||
|
$this->oDb = $oDb;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function setProjectId($iProjectId=0) {
|
||||||
|
if($iProjectId > 0) {
|
||||||
|
$this->iProjectId = $iProjectId;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
/**
|
||||||
|
* Project 1 [-----------------]
|
||||||
|
* Project 2 [---------------------------]
|
||||||
|
* Project 3 [-----------]
|
||||||
|
* Selected Project [-------Project 1-------][------------Project 2-------------][---------------Project 3------------------
|
||||||
|
* Mode --P--][--------B--------][--P--][-----------B---------------][---P---][-----B-----][---------H----------
|
||||||
|
*/
|
||||||
|
$sQuery = "SELECT MAX(id_project) FROM projects WHERE active_to = (SELECT MIN(active_to) FROM projects WHERE active_to > NOW() OR active_to = (SELECT MAX(active_to) FROM projects))";
|
||||||
|
$asResult = $this->oDb->getArrayQuery($sQuery, true);
|
||||||
|
$this->iProjectId = array_shift($asResult);
|
||||||
|
}
|
||||||
|
|
||||||
|
$this->setProjectInfo();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getMode() {
|
||||||
|
return $this->sMode;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProjectId() {
|
||||||
|
return $this->iProjectId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProjectCodeName() {
|
||||||
|
return $this->sCodeName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getActivePeriod($sFromTo='') {
|
||||||
|
return ($sFromTo=='')?$this->asActive:$this->asActive[$sFromTo];
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getProjects($iProjectId=0) {
|
||||||
|
$asInfo = array(
|
||||||
|
'select'=> array(
|
||||||
|
Db::getId(self::PROJ_TABLE)." AS id",
|
||||||
|
'codename',
|
||||||
|
'name',
|
||||||
|
'active_from',
|
||||||
|
'active_to',
|
||||||
|
"IF(NOW() BETWEEN active_from AND active_to, 1, IF(NOW() < active_from, 0, 2)) AS mode",
|
||||||
|
'geofile',
|
||||||
|
'timezone'
|
||||||
|
),
|
||||||
|
'from' => self::PROJ_TABLE
|
||||||
|
);
|
||||||
|
if($iProjectId > 0) $asInfo['constraint'] = array(Db::getId(self::PROJ_TABLE)=>$iProjectId);
|
||||||
|
|
||||||
|
$asProjects = $this->oDb->selectRows($asInfo, 'codename');
|
||||||
|
foreach($asProjects as &$asProject) {
|
||||||
|
switch($asProject['mode']) {
|
||||||
|
case 0: $asProject['mode'] = self::MODE_PREVIZ; break;
|
||||||
|
case 1: $asProject['mode'] = self::MODE_BLOG; break;
|
||||||
|
case 2: $asProject['mode'] = self::MODE_HISTO; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
$sGeoFilePath = self::GEO_FOLDER.$asProject['geofile'];
|
||||||
|
$asProject['geofile'] = file_exists($sGeoFilePath)?$asProject['geofile'].'?'.date("YmdHis", filemtime($sGeoFilePath)):$asProject['geofile'];
|
||||||
|
}
|
||||||
|
return $asProjects;
|
||||||
|
}
|
||||||
|
|
||||||
|
private function setProjectInfo() {
|
||||||
|
$asResult = $this->getProjects($this->iProjectId);
|
||||||
|
$asProject = reset($asResult);
|
||||||
|
|
||||||
|
$this->sMode = $asProject['mode'];
|
||||||
|
$this->asActive = array('from'=>$asProject['active_from'], 'to'=>$asProject['active_to']);
|
||||||
|
$this->sCodeName = key($asResult);
|
||||||
|
$this->sName = $asProject['name'];
|
||||||
|
$this->sGeo = array('file'=>$asProject['geofile'], 'timezone'=>$asProject['timezone']);
|
||||||
|
}
|
||||||
|
}
|
||||||
216
inc/spot.php
216
inc/spot.php
@@ -2,10 +2,6 @@
|
|||||||
|
|
||||||
class Spot extends Main
|
class Spot extends Main
|
||||||
{
|
{
|
||||||
//Spot Mode
|
|
||||||
const MODE_HISTO = 'histo';
|
|
||||||
const MODE_BLOG = 'blog';
|
|
||||||
|
|
||||||
//Spot feed
|
//Spot feed
|
||||||
const FEED_HOOK = 'https://api.findmespot.com/spot-main-web/consumer/rest-api/2.0/public/feed/';
|
const FEED_HOOK = 'https://api.findmespot.com/spot-main-web/consumer/rest-api/2.0/public/feed/';
|
||||||
const FEED_TYPE_XML = '/message.xml';
|
const FEED_TYPE_XML = '/message.xml';
|
||||||
@@ -25,19 +21,27 @@ class Spot extends Main
|
|||||||
|
|
||||||
const FEED_CHUNK_SIZE = 15;
|
const FEED_CHUNK_SIZE = 15;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Active Project
|
||||||
|
* @var Project
|
||||||
|
*/
|
||||||
|
private $oProject;
|
||||||
|
|
||||||
public function __construct($oClassManagement, $sProcessPage)
|
public function __construct($oClassManagement, $sProcessPage)
|
||||||
{
|
{
|
||||||
parent::__construct($oClassManagement, $sProcessPage);
|
$asClasses = array(
|
||||||
$oClassManagement->incClass('cacher', true);
|
array('name'=>'project', 'project'=>true),
|
||||||
|
array('name'=>'cacher', 'project'=>true)
|
||||||
|
);
|
||||||
|
parent::__construct($oClassManagement, $sProcessPage, $asClasses);
|
||||||
|
|
||||||
|
$this->oProject = new Project($this->oDb);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function install()
|
protected function install()
|
||||||
{
|
{
|
||||||
//Install DB
|
//Install DB
|
||||||
$this->oDb->install();
|
$this->oDb->install();
|
||||||
|
|
||||||
//Add feed
|
|
||||||
//$this->oDb->insertRow(self::FEED_TABLE, array('ref_feed_id'=>Settings::FEED_ID));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getSqlOptions()
|
protected function getSqlOptions()
|
||||||
@@ -46,36 +50,44 @@ class Spot extends Main
|
|||||||
(
|
(
|
||||||
'tables' => array
|
'tables' => array
|
||||||
(
|
(
|
||||||
self::MSG_TABLE => array('ref_msg_id', Db::getId(self::FEED_TABLE), 'type', 'latitude', 'longitude', 'timestamp', 'unix_timestamp', 'content', 'battery_state'),
|
self::MSG_TABLE => array('ref_msg_id', Db::getId(self::FEED_TABLE), 'type', 'latitude', 'longitude', 'timestamp', 'unix_timestamp', 'content', 'battery_state'),
|
||||||
self::FEED_TABLE => array('ref_feed_id', Db::getId(self::SPOT_TABLE), 'name', 'description', 'status'),
|
self::FEED_TABLE => array('ref_feed_id', Db::getId(self::SPOT_TABLE), Db::getId(Project::PROJ_TABLE), 'name', 'description', 'status', 'last_update'),
|
||||||
self::SPOT_TABLE => array('ref_spot_id', 'name', 'model'),
|
self::SPOT_TABLE => array('ref_spot_id', 'name', 'model'),
|
||||||
self::POST_TABLE => array('name', 'content')
|
Project::PROJ_TABLE => array('name', 'codename', 'active_from', 'active_to', 'geofile', 'timezone'),
|
||||||
|
self::POST_TABLE => array(Db::getId(Project::PROJ_TABLE), 'name', 'content', 'timestamp')
|
||||||
),
|
),
|
||||||
'types' => array
|
'types' => array
|
||||||
(
|
(
|
||||||
'ref_msg_id' => "INT",
|
'ref_msg_id' => "INT",
|
||||||
'type' => "VARCHAR(20)",
|
'type' => "VARCHAR(20)",
|
||||||
'latitude' => "DECIMAL(7,5)",
|
'latitude' => "DECIMAL(7,5)",
|
||||||
'longitude' => "DECIMAL(8,5)",
|
'longitude' => "DECIMAL(8,5)",
|
||||||
'timestamp' => "DATETIME",
|
'timestamp' => "DATETIME",
|
||||||
'unix_timestamp'=> "INT",
|
'unix_timestamp'=> "INT",
|
||||||
'content' => "LONGTEXT",
|
'content' => "LONGTEXT",
|
||||||
'battery_state' => "VARCHAR(10)",
|
'battery_state' => "VARCHAR(10)",
|
||||||
'ref_spot_id' => "VARCHAR(10)",
|
'ref_spot_id' => "VARCHAR(10)",
|
||||||
'name' => "VARCHAR(100)",
|
'name' => "VARCHAR(100)",
|
||||||
'model' => "VARCHAR(20)",
|
'codename' => "VARCHAR(100)",
|
||||||
'ref_feed_id' => "VARCHAR(40)",
|
'model' => "VARCHAR(20)",
|
||||||
'description' => "VARCHAR(100)",
|
'ref_feed_id' => "VARCHAR(40)",
|
||||||
'status' => "VARCHAR(10)",
|
'description' => "VARCHAR(100)",
|
||||||
|
'status' => "VARCHAR(10)",
|
||||||
|
'active_from' => "DATETIME",
|
||||||
|
'active_to' => "DATETIME",
|
||||||
|
'geofile' => "VARCHAR(50)",
|
||||||
|
'timezone' => "VARCHAR(100)",
|
||||||
|
'last_update' => "DATETIME"
|
||||||
),
|
),
|
||||||
'constraints' => array
|
'constraints' => array
|
||||||
(
|
(
|
||||||
self::MSG_TABLE => "UNIQUE KEY `uni_ref_msg_id` (`ref_msg_id`)",
|
self::MSG_TABLE => "UNIQUE KEY `uni_ref_msg_id` (`ref_msg_id`)",
|
||||||
self::FEED_TABLE => "UNIQUE KEY `uni_ref_feed_id` (`ref_feed_id`)",
|
self::FEED_TABLE => "UNIQUE KEY `uni_ref_feed_id` (`ref_feed_id`)",
|
||||||
self::SPOT_TABLE => "UNIQUE KEY `uni_ref_spot_id` (`ref_spot_id`)",
|
self::SPOT_TABLE => "UNIQUE KEY `uni_ref_spot_id` (`ref_spot_id`)",
|
||||||
self::MSG_TABLE => "INDEX(`ref_msg_id`)",
|
self::MSG_TABLE => "INDEX(`ref_msg_id`)",
|
||||||
self::FEED_TABLE => "INDEX(`ref_feed_id`)",
|
self::FEED_TABLE => "INDEX(`ref_feed_id`)",
|
||||||
self::SPOT_TABLE => "INDEX(`ref_spot_id`)",
|
self::SPOT_TABLE => "INDEX(`ref_spot_id`)",
|
||||||
|
Project::PROJ_TABLE => "UNIQUE KEY `uni_proj_name` (`codename`)",
|
||||||
),
|
),
|
||||||
'cascading_delete' => array
|
'cascading_delete' => array
|
||||||
(
|
(
|
||||||
@@ -89,10 +101,14 @@ class Spot extends Main
|
|||||||
return parent::getMainPage(
|
return parent::getMainPage(
|
||||||
array(
|
array(
|
||||||
'vars' => array(
|
'vars' => array(
|
||||||
'feed_id' => Settings::FEED_ID,
|
'chunk_size' => self::FEED_CHUNK_SIZE,
|
||||||
'chunk_size'=> self::FEED_CHUNK_SIZE,
|
'mapbox_key' => Settings::MAPBOX_KEY,
|
||||||
'mode' => Settings::MODE,
|
'default_project_codename' => $this->oProject->getProjectCodeName(),
|
||||||
'mapbox_key'=> Settings::MAPBOX_KEY
|
'projects' => $this->oProject->getProjects()
|
||||||
|
),
|
||||||
|
'consts' => array(
|
||||||
|
'modes' => Project::MODES,
|
||||||
|
'geo_folder'=> Project::GEO_FOLDER
|
||||||
)
|
)
|
||||||
),
|
),
|
||||||
'index',
|
'index',
|
||||||
@@ -100,16 +116,22 @@ class Spot extends Main
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Managing projects */
|
||||||
|
|
||||||
|
public function setProjectId($iProjectId=0) {
|
||||||
|
$this->oProject->setProjectId($iProjectId);
|
||||||
|
}
|
||||||
|
|
||||||
/* Getting & Storing messages */
|
/* Getting & Storing messages */
|
||||||
|
|
||||||
private function getFeed($sRefFeedId=Settings::FEED_ID)
|
private function getFeed($sRefFeedId)
|
||||||
{
|
{
|
||||||
$sUrl = self::FEED_HOOK.$sRefFeedId.self::FEED_TYPE_JSON;
|
$sUrl = self::FEED_HOOK.$sRefFeedId.self::FEED_TYPE_JSON;
|
||||||
$sContent = file_get_contents($sUrl);
|
$sContent = file_get_contents($sUrl);
|
||||||
return json_decode($sContent, true);
|
return json_decode($sContent, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function updateFeed($sRefFeedId=Settings::FEED_ID)
|
private function updateFeed($sRefFeedId)
|
||||||
{
|
{
|
||||||
$asData = $this->getFeed($sRefFeedId);
|
$asData = $this->getFeed($sRefFeedId);
|
||||||
$asMsgs = $asData['response']['feedMessageResponse']['messages'];
|
$asMsgs = $asData['response']['feedMessageResponse']['messages'];
|
||||||
@@ -122,14 +144,15 @@ class Spot extends Main
|
|||||||
$asSpotInfo = array('ref_spot_id'=>$asFirstMsg['messengerId'], 'name'=>$asFirstMsg['messengerName'], 'model'=>$asFirstMsg['modelId']);
|
$asSpotInfo = array('ref_spot_id'=>$asFirstMsg['messengerId'], 'name'=>$asFirstMsg['messengerName'], 'model'=>$asFirstMsg['modelId']);
|
||||||
$iSpotId = $this->oDb->insertUpdateRow(self::SPOT_TABLE, $asSpotInfo, array('ref_spot_id'));
|
$iSpotId = $this->oDb->insertUpdateRow(self::SPOT_TABLE, $asSpotInfo, array('ref_spot_id'));
|
||||||
|
|
||||||
//Update Feed Info
|
//Update Feed Info and last update date
|
||||||
$asFeedInfo = array(
|
$asFeedInfo = array(
|
||||||
'ref_feed_id' => $sRefFeedId,
|
'ref_feed_id' => $sRefFeedId,
|
||||||
Db::getId(self::SPOT_TABLE) => $iSpotId,
|
Db::getId(self::SPOT_TABLE) => $iSpotId,
|
||||||
'name' => $asFeed['name'],
|
Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId(),
|
||||||
'description' => $asFeed['description'],
|
'name' => $asFeed['name'],
|
||||||
'status' => $asFeed['status'],
|
'description' => $asFeed['description'],
|
||||||
'led' => date(Db::MYSQL_TIMESTAMP) //update with current time
|
'status' => $asFeed['status'],
|
||||||
|
'last_update' => date(Db::TIMESTAMP_FORMAT)
|
||||||
);
|
);
|
||||||
$iFeedId = $this->oDb->insertUpdateRow(self::FEED_TABLE, $asFeedInfo, array('ref_feed_id'));
|
$iFeedId = $this->oDb->insertUpdateRow(self::FEED_TABLE, $asFeedInfo, array('ref_feed_id'));
|
||||||
|
|
||||||
@@ -142,7 +165,7 @@ class Spot extends Main
|
|||||||
'type' => $asMsg['messageType'],
|
'type' => $asMsg['messageType'],
|
||||||
'latitude' => $asMsg['latitude'],
|
'latitude' => $asMsg['latitude'],
|
||||||
'longitude' => $asMsg['longitude'],
|
'longitude' => $asMsg['longitude'],
|
||||||
'timestamp' => date(Db::MYSQL_TIMESTAMP, strtotime($asMsg['dateTime'])), //Stored in Local Time
|
'timestamp' => date(Db::TIMESTAMP_FORMAT, strtotime($asMsg['dateTime'])), //Stored in Local Time
|
||||||
'unix_timestamp' => $asMsg['unixTime'], //Stored in UNIX time
|
'unix_timestamp' => $asMsg['unixTime'], //Stored in UNIX time
|
||||||
'content' => $asMsg['messageContent'],
|
'content' => $asMsg['messageContent'],
|
||||||
'battery_state' => $asMsg['batteryState']
|
'battery_state' => $asMsg['batteryState']
|
||||||
@@ -152,7 +175,7 @@ class Spot extends Main
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public function getMessages($sRefFeedId=Settings::FEED_ID)
|
public function getMessages()
|
||||||
{
|
{
|
||||||
/* Adding another point to test
|
/* Adding another point to test
|
||||||
$test = $this->oDb->selectRow(self::MSG_TABLE, 1);
|
$test = $this->oDb->selectRow(self::MSG_TABLE, 1);
|
||||||
@@ -162,12 +185,7 @@ class Spot extends Main
|
|||||||
$test['longitude'] = '172.1936';
|
$test['longitude'] = '172.1936';
|
||||||
$this->oDb->insertUpdateRow(self::MSG_TABLE, $test, array('ref_msg_id')); */
|
$this->oDb->insertUpdateRow(self::MSG_TABLE, $test, array('ref_msg_id')); */
|
||||||
|
|
||||||
//Check last message & update feed if necessary (max once a day)
|
$asMessages = $this->getSpotMessages();
|
||||||
$sLastMsg = $this->oDb->selectValue(self::FEED_TABLE, 'led', array('ref_feed_id'=>$sRefFeedId));
|
|
||||||
if(Settings::MODE!=self::MODE_HISTO && mb_substr($sLastMsg, 0, 10) != date('Y-m-d')) $this->updateFeed($sRefFeedId);
|
|
||||||
|
|
||||||
//Extract messages
|
|
||||||
$asMessages = array_values($this->getSpotMessages());
|
|
||||||
$bSuccess = !empty($asMessages);
|
$bSuccess = !empty($asMessages);
|
||||||
$sDesc = $bSuccess?'':self::NO_DATA;
|
$sDesc = $bSuccess?'':self::NO_DATA;
|
||||||
|
|
||||||
@@ -202,22 +220,46 @@ class Spot extends Main
|
|||||||
|
|
||||||
private function getSpotMessages()
|
private function getSpotMessages()
|
||||||
{
|
{
|
||||||
$asInfo = array('from'=>self::MSG_TABLE, 'orderBy'=>array('timestamp'=>'ASC'));
|
//Get Feed IDs
|
||||||
if(Settings::MODE==self::MODE_HISTO) {
|
$asFeeds = $this->oDb->selectRows(array(
|
||||||
$asInfo['constraint'] = array('timestamp'=>Settings::HISTO_SPAN);
|
'select' => array(Db::getId(self::FEED_TABLE), 'ref_feed_id', 'last_update'),
|
||||||
$asInfo['constOpe'] = array('timestamp'=>"BETWEEN");
|
'from' => self::FEED_TABLE,
|
||||||
|
'constraint'=> array(Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId()))
|
||||||
|
);
|
||||||
|
|
||||||
|
//Update on Blog Mode and not updated already today
|
||||||
|
$asAllFeedMessages = array();
|
||||||
|
foreach($asFeeds as $asFeed) {
|
||||||
|
|
||||||
|
//Feed updated once a day in Blog Mode
|
||||||
|
if($this->oProject->getMode() == Project::MODE_BLOG && mb_substr($asFeed['last_update'], 0, 10) != date('Y-m-d')) {
|
||||||
|
$this->updateFeed($asFeed['ref_feed_id']);
|
||||||
|
}
|
||||||
|
|
||||||
|
$asInfo = array(
|
||||||
|
'select' => array('id_message', 'ref_msg_id', 'type', 'latitude', 'longitude', 'timestamp', 'unix_timestamp'),
|
||||||
|
'from' => self::MSG_TABLE,
|
||||||
|
'constraint'=> array(
|
||||||
|
Db::getId(Spot::FEED_TABLE) => $asFeed[Db::getId(self::FEED_TABLE)],
|
||||||
|
'timestamp' => $this->oProject->getActivePeriod()),
|
||||||
|
'constOpe' => array(
|
||||||
|
Db::getId(Spot::FEED_TABLE) => "=",
|
||||||
|
'timestamp' => "BETWEEN"),
|
||||||
|
'orderBy' => array('timestamp'=>'ASC'));
|
||||||
|
|
||||||
|
$asMessages = $this->oDb->selectRows($asInfo);
|
||||||
|
foreach($asMessages as $asMessage)
|
||||||
|
{
|
||||||
|
$asMessage['unix_timestamp'] = (int) $asMessage['unix_timestamp'];
|
||||||
|
$asMessage['latitude'] = floatval($asMessage['latitude']);
|
||||||
|
$asMessage['longitude'] = floatval($asMessage['longitude']);
|
||||||
|
$asMessage['relative_time'] = Toolbox::getDateTimeDesc($asMessage['unix_timestamp']);
|
||||||
|
$asMessage['formatted_time'] = date(self::FORMAT_TIME, $asMessage['unix_timestamp']);
|
||||||
|
$asAllFeedMessages[] = $asMessage;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
$asMessages = $this->oDb->selectRows($asInfo);
|
return $asAllFeedMessages;
|
||||||
foreach($asMessages as $iKey=>$asMessage)
|
|
||||||
{
|
|
||||||
$asMessages[$iKey]['unix_timestamp'] = (int) $asMessages[$iKey]['unix_timestamp'];
|
|
||||||
$asMessages[$iKey]['latitude'] = floatval($asMessages[$iKey]['latitude']);
|
|
||||||
$asMessages[$iKey]['longitude'] = floatval($asMessages[$iKey]['longitude']);
|
|
||||||
$asMessages[$iKey]['relative_time'] = Toolbox::getDateTimeDesc($asMessages[$iKey]['unix_timestamp']);
|
|
||||||
$asMessages[$iKey]['formatted_time'] = date(self::FORMAT_TIME, $asMessages[$iKey]['unix_timestamp']);
|
|
||||||
}
|
|
||||||
return $asMessages;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private function getPictures()
|
private function getPictures()
|
||||||
@@ -234,9 +276,8 @@ class Spot extends Main
|
|||||||
//Get/Create thumbnail
|
//Get/Create thumbnail
|
||||||
$sThumbnailPath = self::getPicThumbnail($sPicPath);
|
$sThumbnailPath = self::getPicThumbnail($sPicPath);
|
||||||
|
|
||||||
//Filter on valid time interval (Histo mode only)
|
//Filter on valid time interval
|
||||||
if( Settings::MODE != self::MODE_HISTO ||
|
if($iPicTimeStamp >= strtotime($this->oProject->getActivePeriod('from')) && $iPicTimeStamp <= strtotime($this->oProject->getActivePeriod('to'))) {
|
||||||
$iPicTimeStamp >= strtotime(Settings::HISTO_SPAN['from']) && $iPicTimeStamp <= strtotime(Settings::HISTO_SPAN['to'])) {
|
|
||||||
|
|
||||||
$asPics[] = array(
|
$asPics[] = array(
|
||||||
'path' => $sPicPath,
|
'path' => $sPicPath,
|
||||||
@@ -253,15 +294,19 @@ class Spot extends Main
|
|||||||
|
|
||||||
private function getPosts()
|
private function getPosts()
|
||||||
{
|
{
|
||||||
$asInfo = array('from'=>self::POST_TABLE);
|
$asInfo = array(
|
||||||
if(Settings::MODE==self::MODE_HISTO) {
|
'from' => self::POST_TABLE,
|
||||||
$asInfo['constraint'] = array('led'=>Settings::HISTO_SPAN);
|
'constraint'=> array(Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId()),
|
||||||
$asInfo['constOpe'] = array('led'=>"BETWEEN");
|
'constOpe' => array(Db::getId(Project::PROJ_TABLE) => "=")
|
||||||
|
);
|
||||||
|
if($this->oProject->getMode()==Project::MODE_HISTO) {
|
||||||
|
$asInfo['constraint']['timestamp'] = $this->oProject->getActivePeriod();
|
||||||
|
$asInfo['constOpe']['timestamp'] = "BETWEEN";
|
||||||
}
|
}
|
||||||
|
|
||||||
$asPosts = $this->oDb->selectRows($asInfo);
|
$asPosts = $this->oDb->selectRows($asInfo);
|
||||||
|
|
||||||
foreach($asPosts as &$asPost) {
|
foreach($asPosts as &$asPost) {
|
||||||
$iUnixTimeStamp = strtotime($asPost['led']);
|
$iUnixTimeStamp = strtotime($asPost['timestamp']);
|
||||||
$asPost['unix_timestamp'] = $iUnixTimeStamp;
|
$asPost['unix_timestamp'] = $iUnixTimeStamp;
|
||||||
$asPost['relative_time'] = Toolbox::getDateTimeDesc($iUnixTimeStamp);
|
$asPost['relative_time'] = Toolbox::getDateTimeDesc($iUnixTimeStamp);
|
||||||
$asPost['formatted_time'] = date(self::FORMAT_TIME, $iUnixTimeStamp);
|
$asPost['formatted_time'] = date(self::FORMAT_TIME, $iUnixTimeStamp);
|
||||||
@@ -320,7 +365,12 @@ class Spot extends Main
|
|||||||
|
|
||||||
public function addPost($sName, $sPost)
|
public function addPost($sName, $sPost)
|
||||||
{
|
{
|
||||||
$asData = array('name'=>mb_strtolower(trim($sName)), 'content'=>trim($sPost));
|
$asData = array(
|
||||||
|
Db::getId(Project::PROJ_TABLE) => $this->oProject->getProjectId(),
|
||||||
|
'name' => mb_strtolower(trim($sName)),
|
||||||
|
'content' => trim($sPost),
|
||||||
|
'timestamp' => date(Db::TIMESTAMP_FORMAT)
|
||||||
|
);
|
||||||
$iPostId = $this->oDb->insertRow(self::POST_TABLE, $asData);
|
$iPostId = $this->oDb->insertRow(self::POST_TABLE, $asData);
|
||||||
return self::getJsonResult(($iPostId > 0), '');
|
return self::getJsonResult(($iPostId > 0), '');
|
||||||
}
|
}
|
||||||
@@ -337,6 +387,7 @@ class Spot extends Main
|
|||||||
{
|
{
|
||||||
if(isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] == $this->asContext['serv_name']) {
|
if(isset($_SERVER['HTTP_REFERER']) && $_SERVER['HTTP_REFERER'] == $this->asContext['serv_name']) {
|
||||||
$asDomains = array();
|
$asDomains = array();
|
||||||
|
$sReferer = 'http://spot.lutran.fr';
|
||||||
switch($sMapId) {
|
switch($sMapId) {
|
||||||
case 'mapbox.satellite':
|
case 'mapbox.satellite':
|
||||||
case 'mapbox.streets':
|
case 'mapbox.streets':
|
||||||
@@ -348,10 +399,19 @@ class Spot extends Main
|
|||||||
$sToken = Settings::LINZ_KEY;
|
$sToken = Settings::LINZ_KEY;
|
||||||
$asDomains = array('a', 'b', 'c', 'd');
|
$asDomains = array('a', 'b', 'c', 'd');
|
||||||
break;
|
break;
|
||||||
|
case 'ign.es':
|
||||||
|
$sPattern = 'http://www.ign.es/wmts/mapa-raster?request=getTile&format=image/png&layer=MTN&TileMatrixSet=GoogleMapsCompatible&TileMatrix={z}&TileCol={x}&TileRow={y}';
|
||||||
|
break;
|
||||||
|
case 'ign.fr':
|
||||||
|
$sPattern = 'https://wxs.ign.fr/{token}/geoportail/wmts?LAYER=GEOGRAPHICALGRIDSYSTEMS.MAPS&EXCEPTIONS=text/xml&FORMAT=image/jpeg&SERVICE=WMTS&VERSION=1.0.0&REQUEST=GetTile&STYLE=normal&TILEMATRIXSET=PM&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}';
|
||||||
|
$sToken = Settings::IGN_FR_KEY;
|
||||||
|
$sReferer = 'https://www.visugpx.com/yfJDwfuTlf';
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
$oCacher = new Cacher($sPattern, $sMapId);
|
$oCacher = new Cacher($sPattern, $sMapId);
|
||||||
$oCacher->setToken($sToken);
|
$oCacher->setToken($sToken);
|
||||||
$oCacher->setDomains($asDomains);
|
$oCacher->setDomains($asDomains);
|
||||||
|
$oCacher->setReferer($sReferer);
|
||||||
|
|
||||||
return $oCacher->pushTile($iX, $iY, $iZ);
|
return $oCacher->pushTile($iX, $iY, $iZ);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,12 +22,14 @@ $sName = isset($_GET['name'])?$_GET['name']:'';
|
|||||||
$sContent = isset($_GET['content'])?$_GET['content']:'';
|
$sContent = isset($_GET['content'])?$_GET['content']:'';
|
||||||
$iChunk = isset($_GET['chunk'])?$_GET['chunk']:0;
|
$iChunk = isset($_GET['chunk'])?$_GET['chunk']:0;
|
||||||
$sId = isset($_REQUEST['id'])?$_REQUEST['id']:'';
|
$sId = isset($_REQUEST['id'])?$_REQUEST['id']:'';
|
||||||
|
$iProjectId = isset($_REQUEST['project_id'])?$_REQUEST['project_id']:'';
|
||||||
$iX = isset($_GET['x'])?$_GET['x']:0;
|
$iX = isset($_GET['x'])?$_GET['x']:0;
|
||||||
$iY = isset($_GET['y'])?$_GET['y']:0;
|
$iY = isset($_GET['y'])?$_GET['y']:0;
|
||||||
$iZ = isset($_GET['z'])?$_GET['z']:0;
|
$iZ = isset($_GET['z'])?$_GET['z']:0;
|
||||||
|
|
||||||
//Initiate class
|
//Initiate class
|
||||||
$oSpot = new Spot($oClassManagement, __FILE__);
|
$oSpot = new Spot($oClassManagement, __FILE__);
|
||||||
|
$oSpot->setProjectId($iProjectId);
|
||||||
|
|
||||||
$sResult = '';
|
$sResult = '';
|
||||||
if($sAction!='')
|
if($sAction!='')
|
||||||
@@ -49,6 +51,9 @@ if($sAction!='')
|
|||||||
case 'tile':
|
case 'tile':
|
||||||
$sResult = $oSpot->getTile($sId, $iX, $iY, $iZ);
|
$sResult = $oSpot->getTile($sId, $iX, $iY, $iZ);
|
||||||
break;
|
break;
|
||||||
|
/*case 'sql':
|
||||||
|
$sResult = $oSpot->getDbBuildScript();
|
||||||
|
break;*/
|
||||||
default:
|
default:
|
||||||
$sResult = Spot::getJsonResult(false, Spot::NOT_FOUND);
|
$sResult = Spot::getJsonResult(false, Spot::NOT_FOUND);
|
||||||
}
|
}
|
||||||
|
|||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Binary file not shown.
@@ -1,376 +0,0 @@
|
|||||||
<div id="messages">
|
|
||||||
<div id="map">
|
|
||||||
<div class="loader fa fa-map"></div>
|
|
||||||
</div>
|
|
||||||
<div id="legend">
|
|
||||||
<div class="line te_araroa">Te Araroa</div>
|
|
||||||
<div class="line routeburn">Routeburn Track</div>
|
|
||||||
<div class="line hitchhiking">Hors rando</div>
|
|
||||||
</div>
|
|
||||||
<div id="feed">
|
|
||||||
<div id="posts">
|
|
||||||
<div id="poster"></div>
|
|
||||||
<div id="posts_list"></div>
|
|
||||||
<div id="loading"></div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<script type="text/javascript" src="script/lightbox.min.js"></script>
|
|
||||||
<script type="text/javascript">
|
|
||||||
oSpot.pageInit = function(asHash)
|
|
||||||
{
|
|
||||||
self.tmp('$Map', $('#map'));
|
|
||||||
|
|
||||||
//Add Loading
|
|
||||||
var asLoading = {
|
|
||||||
type: 'loading',
|
|
||||||
formatted_time: '',
|
|
||||||
relative_time: '',
|
|
||||||
displayed_id: 'Chargement...'
|
|
||||||
};
|
|
||||||
getPost(asLoading).appendTo($('#loading'));
|
|
||||||
|
|
||||||
self.get('messages', function(oMessages){
|
|
||||||
|
|
||||||
//Build Feed
|
|
||||||
self.tmp('updatable', true);
|
|
||||||
self.tmp('out-of-data', 'boolean');
|
|
||||||
updateFeed(true);
|
|
||||||
self.tmp('simple-bar', new SimpleBar($('#posts')[0]));
|
|
||||||
self.tmp('simple-bar').getScrollElement().addEventListener('scroll', onFeedScroll);
|
|
||||||
self.tmp('infowindow', 'boolean');
|
|
||||||
self.tmp('map_offset', -0.3);
|
|
||||||
self.tmp('tile_api', '?a=tile&id={id}&z={z}&x={x}&y={y}');
|
|
||||||
|
|
||||||
//Centering map
|
|
||||||
var agCenter = {lat:0, lng:0};
|
|
||||||
var iZoom = 0;
|
|
||||||
if(self.vars('mode')=='blog')
|
|
||||||
{
|
|
||||||
//on last message
|
|
||||||
var oLastMsg = oMessages[oMessages.length-1];
|
|
||||||
agCenter.lat = oLastMsg.latitude;
|
|
||||||
agCenter.lng = oLastMsg.longitude;
|
|
||||||
iZoom = 12;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var iMapPadding = 0.05;
|
|
||||||
var iMinLat, iMaxLat, iMinLng, iMaxLng;
|
|
||||||
iMinLat = iMinLng = 180;
|
|
||||||
iMaxLat = iMaxLng = -180;
|
|
||||||
$.each(oMessages, function(iKey, oMsg){
|
|
||||||
iMinLat = Math.min(iMinLat, oMsg.latitude);
|
|
||||||
iMaxLat = Math.max(iMaxLat, oMsg.latitude);
|
|
||||||
iMinLng = Math.min(iMinLng, oMsg.longitude);
|
|
||||||
iMaxLng = Math.max(iMaxLng, oMsg.longitude);
|
|
||||||
});
|
|
||||||
|
|
||||||
//Get Marker bounds
|
|
||||||
var oSouthWest = L.latLng(iMinLat, iMinLng);
|
|
||||||
var oNorthEast = L.latLng(iMaxLat, iMaxLng);
|
|
||||||
oMarkerBounds = new L.LatLngBounds(oSouthWest, oNorthEast);
|
|
||||||
agCenter = oMarkerBounds.getCenter();
|
|
||||||
|
|
||||||
//Calculate adequate zoom (map.fitBounds is dezooming too much)
|
|
||||||
var oMapDim = {
|
|
||||||
height: self.tmp('$Map').height()*(1 - iMapPadding),
|
|
||||||
width: self.tmp('$Map').width()*(1 + self.tmp('map_offset') - iMapPadding)
|
|
||||||
};
|
|
||||||
iZoom = getBoundsZoomLevel(oMarkerBounds, oMapDim);
|
|
||||||
}
|
|
||||||
|
|
||||||
//Tile layers
|
|
||||||
var oMapBoxSat = L.tileLayer(self.tmp('tile_api'), {id: 'mapbox.satellite', minZoom: 0, maxZoom: 19}),
|
|
||||||
//oOpenTopoMap = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {id: 'OpenTopoMap', minZoom: 2, maxZoom: 19});
|
|
||||||
//oMapBoxStreet = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token='+self.vars('mapbox_key'), {id: 'mapbox.streets'}),
|
|
||||||
oLinz = L.tileLayer(self.tmp('tile_api'), {id: 'linz', maxZoom: 17, continuousWorld: true, attribution: 'Sourced from LINZ. CC BY 4.0'});
|
|
||||||
//oGoogleSatellite = L.tileLayer('https://mt.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {id: 'GoogleSatellite', minZoom: 1, maxZoom: 22});
|
|
||||||
|
|
||||||
//Map
|
|
||||||
var oMap = L.map(self.tmp('$Map')[0], {
|
|
||||||
center: agCenter,
|
|
||||||
zoom: iZoom,
|
|
||||||
layers: [oMapBoxSat],
|
|
||||||
attributionControl: false,
|
|
||||||
zoomControl: false
|
|
||||||
});
|
|
||||||
|
|
||||||
//Controls
|
|
||||||
L.control.layers({'Satellite': oMapBoxSat, 'LINZ': oLinz}, null, {position: 'topleft'}).addTo(oMap);
|
|
||||||
|
|
||||||
//Te Araroa track
|
|
||||||
$.ajax({
|
|
||||||
dataType: 'json',
|
|
||||||
url: 'kml/TeAraroaTrail_simplified.geojson',
|
|
||||||
mimeType: 'application/json',
|
|
||||||
success: function(aoTracks) {
|
|
||||||
//Get track colors
|
|
||||||
var asColors = {};
|
|
||||||
$('#legend').find('.line').each(function(iKey, oLegend){
|
|
||||||
var $Legend = $(oLegend);
|
|
||||||
asColors[$Legend.attr('class').replace('line', '').trim()] = $Legend.css('border-top-color');
|
|
||||||
});
|
|
||||||
|
|
||||||
//Assign track color & popup
|
|
||||||
L.geoJson(aoTracks, {
|
|
||||||
style: function(oTrack) {return {color: asColors[oTrack.properties.type], weight: 4, opacity: 1};}
|
|
||||||
})
|
|
||||||
.bindPopup(function(oLayer) {
|
|
||||||
var asProperties = oLayer.feature.properties;
|
|
||||||
var $Tooltip = $('<div>', {'class':'track_tooltip'});
|
|
||||||
$('<p>', {'class':'name'}).text(asProperties.Name).appendTo($Tooltip);
|
|
||||||
if(asProperties.Name != asProperties.description) $('<p>', {'class':'description'}).text(asProperties.description).appendTo($Tooltip);
|
|
||||||
return $Tooltip[0];
|
|
||||||
})
|
|
||||||
.addTo(oMap);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Building messages
|
|
||||||
$.each(oMessages, function(iKey, oMsg){
|
|
||||||
|
|
||||||
//Marker
|
|
||||||
var oMarker = L.marker(L.latLng(oMsg.latitude, oMsg.longitude), {
|
|
||||||
id: oMsg.id_message,
|
|
||||||
riseOnHover: true,
|
|
||||||
icon: L.icon({
|
|
||||||
iconUrl: (iKey%2==0)?'images/footprint_alt.png':'images/footprint.png',
|
|
||||||
iconSize: [32, 37],
|
|
||||||
iconAnchor: [16, 37]
|
|
||||||
})
|
|
||||||
}).addTo(oMap);
|
|
||||||
|
|
||||||
//Marker events
|
|
||||||
oMarker.on({
|
|
||||||
mouseover: function(){
|
|
||||||
this.openPopup();
|
|
||||||
},
|
|
||||||
/*mouseout: function(){
|
|
||||||
|
|
||||||
},*/
|
|
||||||
click: function(oPoint){
|
|
||||||
self.tmp('map').setOffsetView(self.tmp('map_offset'), oPoint.latlng, 15);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
//Tooltip
|
|
||||||
$Tooltip = $('<div>', {'class':'info-window'})
|
|
||||||
.append($('<h1>').append('Message '+oMsg.type+' #'+oMsg.id_message))
|
|
||||||
.append($('<p>', {'class':'time'}).addIcon('fa-clock-o').append(oMsg.formatted_time+' ('+oMsg.relative_time+')'))
|
|
||||||
.append($('<p>', {'class':'coordinates'}).addIcon('fa-compass').append('Lat : '+oMsg.latitude+', Lng : '+oMsg.longitude));
|
|
||||||
|
|
||||||
//Tooltip pictures
|
|
||||||
if(oMsg.pics) {
|
|
||||||
var $Pics = $('<div>', {'class':'pics'});
|
|
||||||
$.each(oMsg.pics, function(iKey, asPic){
|
|
||||||
$Pics.append($('<a>', {href: asPic.path, 'data-lightbox': self.consts.title, 'data-title': asPic.formatted_time})
|
|
||||||
.append($('<img>', {'src': asPic.thumb_path})));
|
|
||||||
});
|
|
||||||
$Tooltip
|
|
||||||
.append($('<p>').addIcon('fa-image').append('Photos'))
|
|
||||||
.append($Pics);
|
|
||||||
}
|
|
||||||
|
|
||||||
oMarker.bindPopup($Tooltip[0], {
|
|
||||||
maxWidth: 1000,
|
|
||||||
keepInView: true,
|
|
||||||
closeOnClick: true,
|
|
||||||
offset: new L.Point(0, -30)
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
//Recenter map to be at the center of 70% (map_offset) of the page, 30% being used by posts
|
|
||||||
if(self.vars('mode')!='blog') oMap.setOffsetView(self.tmp('map_offset'));
|
|
||||||
|
|
||||||
//Legend
|
|
||||||
var oLegend = L.control({position: 'bottomright'});
|
|
||||||
oLegend.onAdd = function(oMap) {return L.DomUtil.get('legend');};
|
|
||||||
oLegend.addTo(oMap);
|
|
||||||
|
|
||||||
self.tmp('map', oMap);
|
|
||||||
});
|
|
||||||
|
|
||||||
//Post
|
|
||||||
if(self.vars('mode')=='histo') $('#poster').hide();
|
|
||||||
else {
|
|
||||||
var asPoster = {
|
|
||||||
type: 'poster',
|
|
||||||
formatted_time: '',
|
|
||||||
relative_time: 'Nouveau message'
|
|
||||||
};
|
|
||||||
getPost(asPoster).appendTo($('#poster'));
|
|
||||||
|
|
||||||
$('#name').defaultVal('Nom...');
|
|
||||||
$('#post').defaultVal('Ton message...');
|
|
||||||
$('#submit').click(function(){
|
|
||||||
if($('#poster').checkForm())
|
|
||||||
{
|
|
||||||
self.get(
|
|
||||||
'add_post',
|
|
||||||
function()
|
|
||||||
{
|
|
||||||
$('#name').val('');
|
|
||||||
$('#post').val('');
|
|
||||||
updateFeed(true);
|
|
||||||
},
|
|
||||||
{name:$('#name').val(), content:$('#post').val()}
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
function getBoundsZoomLevel(bounds, mapDim) {
|
|
||||||
var WORLD_DIM = { height: 256, width: 256 };
|
|
||||||
var ZOOM_MAX = 21;
|
|
||||||
|
|
||||||
function latRad(lat) {
|
|
||||||
var sin = Math.sin(lat * Math.PI / 180);
|
|
||||||
var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
|
|
||||||
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
|
|
||||||
}
|
|
||||||
|
|
||||||
function zoom(mapPx, worldPx, fraction) {
|
|
||||||
return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
|
|
||||||
}
|
|
||||||
|
|
||||||
var ne = bounds.getNorthEast();
|
|
||||||
var sw = bounds.getSouthWest();
|
|
||||||
|
|
||||||
var latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI;
|
|
||||||
|
|
||||||
var lngDiff = ne.lng - sw.lng;
|
|
||||||
var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
|
|
||||||
|
|
||||||
var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
|
|
||||||
var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);
|
|
||||||
|
|
||||||
return Math.min(latZoom, lngZoom, ZOOM_MAX);
|
|
||||||
}
|
|
||||||
|
|
||||||
L.Map.include({
|
|
||||||
setOffsetView: function (iOffsetRatioX, oCenter, iZoomLevel) {
|
|
||||||
var oCenter = (typeof oCenter == 'object')?$.extend({}, oCenter):this.getCenter();
|
|
||||||
iZoomLevel = iZoomLevel || this.getZoom();
|
|
||||||
|
|
||||||
var oBounds = this.getBounds();
|
|
||||||
var iOffsetX = (oBounds.getEast() - oBounds.getWest()) * iOffsetRatioX / ( 2 * Math.pow(2, iZoomLevel - this.getZoom()));
|
|
||||||
oCenter.lng = oCenter.lng - iOffsetX;
|
|
||||||
|
|
||||||
this.setView(oCenter, iZoomLevel);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
function onFeedScroll(){
|
|
||||||
var $Box = $(this);
|
|
||||||
var $BoxContent = $Box.find('.simplebar-content');
|
|
||||||
if(($Box.scrollTop() + $(window).height()) / $BoxContent.height() >= 0.8) updateFeed();
|
|
||||||
}
|
|
||||||
|
|
||||||
function updateFeed(bFirstChunk)
|
|
||||||
{
|
|
||||||
bFirstChunk = bFirstChunk || false;
|
|
||||||
|
|
||||||
if(self.tmp('updatable')) {
|
|
||||||
if(!self.tmp('out-of-data') || bFirstChunk) {
|
|
||||||
var $Posts = $('#posts_list');
|
|
||||||
if(bFirstChunk===true) {
|
|
||||||
$Posts.empty();
|
|
||||||
self.tmp('news_chunk', 0);
|
|
||||||
}
|
|
||||||
|
|
||||||
self.tmp('updatable', false);
|
|
||||||
$('#loading').fadeIn('fast');
|
|
||||||
|
|
||||||
self.get('feed', function(asData) {
|
|
||||||
|
|
||||||
$.each(asData, function(iKey, asPost){
|
|
||||||
getPost(asPost).appendTo($Posts);
|
|
||||||
});
|
|
||||||
|
|
||||||
//oSimpleBar.recalculate();
|
|
||||||
|
|
||||||
self.tmp('news_chunk', self.tmp('news_chunk') + 1);
|
|
||||||
self.tmp('out-of-data', Object.keys(asData).length != self.vars('chunk_size'));
|
|
||||||
self.tmp('updatable', true);
|
|
||||||
$('#loading').fadeOut('fast');
|
|
||||||
}, {
|
|
||||||
'chunk': self.tmp('news_chunk')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(bFirstChunk) { //Delaying important data load
|
|
||||||
if(typeof oUpdateTimer != 'undefined') clearTimeout(oUpdateTimer);
|
|
||||||
oUpdateTimer = setTimeout(function(){updateFeed(true);}, 200);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function getPost(asPost) {
|
|
||||||
var $Post = $('<div>', {'class':'post '+asPost.type});
|
|
||||||
var sRelTime = (self.vars('mode')=='histo')?asPost.formatted_time.substr(0, 10):asPost.relative_time;
|
|
||||||
var sAbsTime = asPost.formatted_time;
|
|
||||||
var $Body = {};
|
|
||||||
switch(asPost.type)
|
|
||||||
{
|
|
||||||
case 'message':
|
|
||||||
$Body = $('<div>')
|
|
||||||
.append($('<p>').addIcon('fa-compass', true).append('Latitude '+asPost.latitude+', Longitude '+asPost.longitude))
|
|
||||||
.append($('<p>').addIcon('fa-clock-o', true).append(sAbsTime))
|
|
||||||
.append(
|
|
||||||
$('<img>', {'class':'staticmap', title: 'Click pour zoomer', src: getStaticMapUrl(asPost.latitude, asPost.longitude)})
|
|
||||||
.data('lat', asPost.latitude)
|
|
||||||
.data('lng', asPost.longitude)
|
|
||||||
.click(function(){
|
|
||||||
var $This = $(this);
|
|
||||||
var oCenter = L.latLng(parseFloat($This.data('lat')), parseFloat($This.data('lng')));
|
|
||||||
self.tmp('map').setOffsetView(self.tmp('map_offset'), oCenter, 13);
|
|
||||||
})
|
|
||||||
);
|
|
||||||
sClass = 'compass';
|
|
||||||
break;
|
|
||||||
case 'picture':
|
|
||||||
var $Image = $('<img>', {'src': asPost.thumb_path/*, 'style':'transform:rotate('+asPost.rotate+'deg);'*/});
|
|
||||||
$Body = $('<a>', {href: asPost.path, 'data-lightbox': self.consts.title, 'data-title': sAbsTime}).append($Image);
|
|
||||||
sClass = 'image';
|
|
||||||
break;
|
|
||||||
case 'post':
|
|
||||||
$Body = $('<div>')
|
|
||||||
.append($('<p>', {'class':'message'}).text(asPost.content))
|
|
||||||
.append($('<p>', {'class':'signature'}).text('-- '+asPost.formatted_name));
|
|
||||||
sClass = 'comment';
|
|
||||||
break;
|
|
||||||
case 'poster':
|
|
||||||
$Body = $('<p>', {'class':'message'})
|
|
||||||
.append($('<input>', {type:'text', id:'post', name:'post'}))
|
|
||||||
.append($('<input>', {type:'text', id:'name', name:'name'}))
|
|
||||||
.append($('<button>', {type:'button', id:'submit', name:'submit'}).addIcon('fa-send'));
|
|
||||||
sClass = 'comment';
|
|
||||||
break;
|
|
||||||
case 'loading':
|
|
||||||
$Body = $('<p>', {'class':'spinner'}).addIcon('fa-spin fa-spinner');
|
|
||||||
sClass = 'tasks';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
$Post
|
|
||||||
.append($('<div>', {'class':'header'})
|
|
||||||
.append($('<span>', {'class':'index'}).addIcon('fa-'+sClass))
|
|
||||||
.append($('<span>', {'class':'time', 'title':sAbsTime}).text(sRelTime)))
|
|
||||||
.append($('<div>', {'class':'body'}).append($Body));
|
|
||||||
|
|
||||||
if(asPost.displayed_id) $Post.find('.index').append(' '+asPost.displayed_id);
|
|
||||||
|
|
||||||
return $Post;
|
|
||||||
}
|
|
||||||
|
|
||||||
function getStaticMapUrl(oCenterLat, oCenterLng) {
|
|
||||||
var asParams = [
|
|
||||||
'https://api.mapbox.com/v4/mapbox.satellite', //Domain
|
|
||||||
'url-http%3A%2F%2Fspot.lutran.fr%2Fimages%2Ffootprint.png('+oCenterLng+','+oCenterLat+')', //Marker
|
|
||||||
oCenterLng+','+oCenterLat+',13', //Center + zoom
|
|
||||||
'400x300.png?access_token='+self.vars('mapbox_key') //Image size + access token
|
|
||||||
];
|
|
||||||
|
|
||||||
return asParams.join('/');
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
385
masks/project.html
Executable file
385
masks/project.html
Executable file
@@ -0,0 +1,385 @@
|
|||||||
|
<div id="messages">
|
||||||
|
<div id="map">
|
||||||
|
<div class="loader fa fa-map"></div>
|
||||||
|
</div>
|
||||||
|
<div id="legend">
|
||||||
|
<div class="line main">Trajet principal</div>
|
||||||
|
<div class="line off-track">Variante</div>
|
||||||
|
<div class="line hitchhiking">Hors rando</div>
|
||||||
|
</div>
|
||||||
|
<div id="feed">
|
||||||
|
<div id="posts">
|
||||||
|
<div id="poster"></div>
|
||||||
|
<div id="posts_list"></div>
|
||||||
|
<div id="loading"></div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<script type="text/javascript" src="script/lightbox.min.js"></script>
|
||||||
|
<script type="text/javascript">
|
||||||
|
self.onSamePageMove = function(asHash){
|
||||||
|
self.tmp('map').remove();
|
||||||
|
self.tmp('$Map').empty();
|
||||||
|
self.pageInit(asHash);
|
||||||
|
return false;
|
||||||
|
};
|
||||||
|
|
||||||
|
oSpot.pageInit = function(asHash)
|
||||||
|
{
|
||||||
|
//Set active project
|
||||||
|
if(asHash.items.length==0) {
|
||||||
|
self.setHash(asHash.page, [self.vars('default_project_codename')]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
self.vars('project', self.vars(['projects', asHash.items[0]]));
|
||||||
|
self.tmp('$Map', $('#map'));
|
||||||
|
self.tmp('tracktype-colors', 'object');
|
||||||
|
|
||||||
|
//Assign Track Type Colors
|
||||||
|
$('#legend').find('.line').each(function(iKey, oLegend){
|
||||||
|
var $Legend = $(oLegend);
|
||||||
|
var sTrackType = $Legend.attr('class').replace('line', '').trim();
|
||||||
|
self.tmp(['tracktype-colors', sTrackType], $Legend.css('border-top-color'));
|
||||||
|
});
|
||||||
|
|
||||||
|
//Add "Loading" Post
|
||||||
|
var asLoading = {
|
||||||
|
type: 'loading',
|
||||||
|
formatted_time: '',
|
||||||
|
relative_time: '',
|
||||||
|
displayed_id: 'Chargement...'
|
||||||
|
};
|
||||||
|
getPost(asLoading).appendTo($('#loading'));
|
||||||
|
|
||||||
|
//Spot Messages
|
||||||
|
$.when(
|
||||||
|
self.get(
|
||||||
|
'messages',
|
||||||
|
function(){},
|
||||||
|
{project_id: self.vars(['project', 'id'])},
|
||||||
|
function(e){console.log(e);}
|
||||||
|
),
|
||||||
|
$.ajax({
|
||||||
|
dataType: 'json',
|
||||||
|
url: self.consts.geo_folder+self.vars(['project', 'geofile']),
|
||||||
|
mimeType: 'application/json'
|
||||||
|
})
|
||||||
|
).done(function(oMessages, aoTracks) {
|
||||||
|
buildSpotMessages(oMessages[0]['data'], aoTracks[0]);
|
||||||
|
});
|
||||||
|
|
||||||
|
//Posts
|
||||||
|
updateFeed(true);
|
||||||
|
initPosts();
|
||||||
|
};
|
||||||
|
|
||||||
|
function buildSpotMessages(oMessages, aoTracks){
|
||||||
|
|
||||||
|
//Build Feed
|
||||||
|
self.tmp('updatable', true);
|
||||||
|
self.tmp('out-of-data', 'boolean');
|
||||||
|
self.tmp('simple-bar', new SimpleBar($('#posts')[0]));
|
||||||
|
self.tmp('simple-bar').getScrollElement().addEventListener('scroll', onFeedScroll);
|
||||||
|
self.tmp('infowindow', 'boolean');
|
||||||
|
self.tmp('feed_width', $('#feed').outerWidth(true));
|
||||||
|
self.tmp('map_offset', -1 * self.tmp('feed_width') / $('body').outerWidth(true));
|
||||||
|
self.tmp('map_padding', 0.05);
|
||||||
|
self.tmp('tile_api', '?a=tile&id={id}&z={z}&x={x}&y={y}');
|
||||||
|
|
||||||
|
//Tile layers
|
||||||
|
var oMapBoxSat = L.tileLayer(self.tmp('tile_api'), {id: 'mapbox.satellite', minZoom: 0, maxZoom: 19}),
|
||||||
|
//oOpenTopoMap = L.tileLayer('https://{s}.tile.opentopomap.org/{z}/{x}/{y}.png', {id: 'OpenTopoMap', minZoom: 2, maxZoom: 19});
|
||||||
|
//oMapBoxStreet = L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token='+self.vars('mapbox_key'), {id: 'mapbox.streets'}),
|
||||||
|
oIgnSpain = L.tileLayer(self.tmp('tile_api'), {id: 'ign.es', minZoom: 1, maxZoom: 20}),
|
||||||
|
oIgnFrance = L.tileLayer(self.tmp('tile_api'), {id: 'ign.fr', minZoom: 0, maxZoom: 18, tileSize: 256}),
|
||||||
|
oLinz = L.tileLayer(self.tmp('tile_api'), {id: 'linz', maxZoom: 17, continuousWorld: true, attribution: 'Sourced from LINZ. CC BY 4.0'});
|
||||||
|
//oGoogleSatellite = L.tileLayer('https://mt.google.com/vt/lyrs=y&x={x}&y={y}&z={z}', {id: 'GoogleSatellite', minZoom: 1, maxZoom: 22});
|
||||||
|
|
||||||
|
//Map
|
||||||
|
var oMap = L.map(self.tmp('$Map')[0], {
|
||||||
|
//center: agCenter,
|
||||||
|
//zoom: iZoom,
|
||||||
|
layers: [oMapBoxSat],
|
||||||
|
attributionControl: false,
|
||||||
|
zoomControl: false
|
||||||
|
});
|
||||||
|
|
||||||
|
//Tracks, colors & popup
|
||||||
|
var oTracks = L.geoJson(aoTracks, {
|
||||||
|
style: function(oTrack) {return {color: self.tmp(['tracktype-colors', oTrack.properties.type]), weight: 4, opacity: 1};}
|
||||||
|
})
|
||||||
|
.bindPopup(function(oLayer) {
|
||||||
|
var asProperties = oLayer.feature.properties;
|
||||||
|
var $Tooltip = $('<div>', {'class':'track_tooltip'});
|
||||||
|
$('<p>', {'class':'name'}).text(asProperties.name).appendTo($Tooltip);
|
||||||
|
if(asProperties.Name != asProperties.description) $('<p>', {'class':'description'}).text(asProperties.description).appendTo($Tooltip);
|
||||||
|
return $Tooltip[0];
|
||||||
|
})
|
||||||
|
.addTo(oMap);
|
||||||
|
|
||||||
|
//Centering map
|
||||||
|
if(self.vars(['project', 'mode'])==self.consts.modes.blog)
|
||||||
|
{
|
||||||
|
//Zoom on last message
|
||||||
|
var oLastMsg = oMessages[oMessages.length-1];
|
||||||
|
oMap.setView(L.latLng(oLastMsg.latitude, oLastMsg.longitude), 15);
|
||||||
|
|
||||||
|
//Recenter map to be at the center of 70% (map_offset) of the page, 30% being used by posts
|
||||||
|
oMap.setOffsetView(self.tmp('map_offset'));
|
||||||
|
}
|
||||||
|
else oMap.fitBounds(oTracks.getBounds(), {paddingTopLeft: L.point(5, 42), paddingBottomRight: L.point(self.tmp('feed_width')+5, 5)});
|
||||||
|
|
||||||
|
//Controls
|
||||||
|
L.control.layers({
|
||||||
|
'Satellite': oMapBoxSat,
|
||||||
|
'IGN (France)': oIgnFrance,
|
||||||
|
'IGN (Espagne)': oIgnSpain,
|
||||||
|
'LINZ (Nouvelle-Zélande)': oLinz
|
||||||
|
}, null, {position: 'topleft'}).addTo(oMap);
|
||||||
|
|
||||||
|
//Building messages
|
||||||
|
$.each(oMessages, function(iKey, oMsg){
|
||||||
|
|
||||||
|
//Marker
|
||||||
|
var oMarker = L.marker(L.latLng(oMsg.latitude, oMsg.longitude), {
|
||||||
|
id: oMsg.id_message,
|
||||||
|
riseOnHover: true,
|
||||||
|
icon: L.icon({
|
||||||
|
iconUrl: (iKey%2==0)?'images/footprint_alt.png':'images/footprint.png',
|
||||||
|
iconSize: [32, 37],
|
||||||
|
iconAnchor: [16, 37]
|
||||||
|
})
|
||||||
|
}).addTo(oMap);
|
||||||
|
|
||||||
|
//Marker events
|
||||||
|
oMarker.on({
|
||||||
|
mouseover: function(){
|
||||||
|
this.openPopup();
|
||||||
|
},
|
||||||
|
/*mouseout: function(){
|
||||||
|
|
||||||
|
},*/
|
||||||
|
click: function(oPoint){
|
||||||
|
self.tmp('map').setOffsetView(self.tmp('map_offset'), oPoint.latlng, 15);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
//Tooltip
|
||||||
|
$Tooltip = $('<div>', {'class':'info-window'})
|
||||||
|
.append($('<h1>').append('Message '+oMsg.type+' #'+oMsg.id_message))
|
||||||
|
.append($('<p>', {'class':'time'}).addIcon('fa-clock-o').append(oMsg.formatted_time+' ('+oMsg.relative_time+')'))
|
||||||
|
.append($('<p>', {'class':'coordinates'}).addIcon('fa-compass').append('Lat : '+oMsg.latitude+', Lng : '+oMsg.longitude));
|
||||||
|
|
||||||
|
//Tooltip pictures
|
||||||
|
if(oMsg.pics) {
|
||||||
|
var $Pics = $('<div>', {'class':'pics'});
|
||||||
|
$.each(oMsg.pics, function(iKey, asPic){
|
||||||
|
$Pics.append($('<a>', {href: asPic.path, 'data-lightbox': self.consts.title, 'data-title': asPic.formatted_time})
|
||||||
|
.append($('<img>', {'src': asPic.thumb_path})));
|
||||||
|
});
|
||||||
|
$Tooltip
|
||||||
|
.append($('<p>').addIcon('fa-image').append('Photos'))
|
||||||
|
.append($Pics);
|
||||||
|
}
|
||||||
|
|
||||||
|
oMarker.bindPopup($Tooltip[0], {
|
||||||
|
maxWidth: 1000,
|
||||||
|
keepInView: true,
|
||||||
|
closeOnClick: true,
|
||||||
|
offset: new L.Point(0, -30)
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
//Legend
|
||||||
|
var oLegend = L.control({position: 'bottomright'});
|
||||||
|
oLegend.onAdd = function(oMap) {return L.DomUtil.get('legend');};
|
||||||
|
oLegend.addTo(oMap);
|
||||||
|
|
||||||
|
self.tmp('map', oMap);
|
||||||
|
}
|
||||||
|
|
||||||
|
function initPosts() {
|
||||||
|
if(self.vars(['project', 'mode'])==self.consts.modes.histo) $('#poster').hide();
|
||||||
|
else {
|
||||||
|
var asPoster = {
|
||||||
|
type: 'poster',
|
||||||
|
formatted_time: '',
|
||||||
|
relative_time: 'Nouveau message'
|
||||||
|
};
|
||||||
|
getPost(asPoster).appendTo($('#poster'));
|
||||||
|
|
||||||
|
//$('#name').defaultVal('Nom...');
|
||||||
|
//$('#post').defaultVal('Ton message...');
|
||||||
|
$('#submit').click(function(){
|
||||||
|
if($('#poster').checkForm())
|
||||||
|
{
|
||||||
|
self.get(
|
||||||
|
'add_post',
|
||||||
|
function()
|
||||||
|
{
|
||||||
|
$('#name').val('');
|
||||||
|
$('#post').val('');
|
||||||
|
updateFeed(true);
|
||||||
|
},
|
||||||
|
{
|
||||||
|
project_id: self.vars(['project', 'id']),
|
||||||
|
name: $('#name').val(),
|
||||||
|
content: $('#post').val()
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getBoundsZoomLevel(bounds, mapDim) {
|
||||||
|
var WORLD_DIM = { height: 256, width: 256 };
|
||||||
|
var ZOOM_MAX = 21;
|
||||||
|
|
||||||
|
function latRad(lat) {
|
||||||
|
var sin = Math.sin(lat * Math.PI / 180);
|
||||||
|
var radX2 = Math.log((1 + sin) / (1 - sin)) / 2;
|
||||||
|
return Math.max(Math.min(radX2, Math.PI), -Math.PI) / 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
function zoom(mapPx, worldPx, fraction) {
|
||||||
|
return Math.floor(Math.log(mapPx / worldPx / fraction) / Math.LN2);
|
||||||
|
}
|
||||||
|
|
||||||
|
var ne = bounds.getNorthEast();
|
||||||
|
var sw = bounds.getSouthWest();
|
||||||
|
|
||||||
|
var latFraction = (latRad(ne.lat) - latRad(sw.lat)) / Math.PI;
|
||||||
|
|
||||||
|
var lngDiff = ne.lng - sw.lng;
|
||||||
|
var lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360;
|
||||||
|
|
||||||
|
var latZoom = zoom(mapDim.height, WORLD_DIM.height, latFraction);
|
||||||
|
var lngZoom = zoom(mapDim.width, WORLD_DIM.width, lngFraction);
|
||||||
|
|
||||||
|
return Math.min(latZoom, lngZoom, ZOOM_MAX);
|
||||||
|
}
|
||||||
|
|
||||||
|
L.Map.include({
|
||||||
|
setOffsetView: function (iOffsetRatioX, oCenter, iZoomLevel) {
|
||||||
|
var oCenter = (typeof oCenter == 'object')?$.extend({}, oCenter):this.getCenter();
|
||||||
|
iZoomLevel = iZoomLevel || this.getZoom();
|
||||||
|
|
||||||
|
var oBounds = this.getBounds();
|
||||||
|
var iOffsetX = (oBounds.getEast() - oBounds.getWest()) * iOffsetRatioX / ( 2 * Math.pow(2, iZoomLevel - this.getZoom()));
|
||||||
|
oCenter.lng = oCenter.lng - iOffsetX;
|
||||||
|
|
||||||
|
this.setView(oCenter, iZoomLevel);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function onFeedScroll(){
|
||||||
|
var $Box = $(this);
|
||||||
|
var $BoxContent = $Box.find('.simplebar-content');
|
||||||
|
if(($Box.scrollTop() + $(window).height()) / $BoxContent.height() >= 0.8) updateFeed();
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateFeed(bFirstChunk)
|
||||||
|
{
|
||||||
|
bFirstChunk = bFirstChunk || false;
|
||||||
|
|
||||||
|
if(self.tmp('updatable')) {
|
||||||
|
if(!self.tmp('out-of-data') || bFirstChunk) {
|
||||||
|
var $Posts = $('#posts_list');
|
||||||
|
if(bFirstChunk===true) {
|
||||||
|
$Posts.empty();
|
||||||
|
self.tmp('news_chunk', 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
self.tmp('updatable', false);
|
||||||
|
$('#loading').fadeIn('fast');
|
||||||
|
|
||||||
|
self.get('feed', function(asData) {
|
||||||
|
$('#loading').hide();
|
||||||
|
$.each(asData, function(iKey, asPost){
|
||||||
|
getPost(asPost).appendTo($Posts);
|
||||||
|
});
|
||||||
|
|
||||||
|
self.tmp('news_chunk', self.tmp('news_chunk') + 1);
|
||||||
|
self.tmp('out-of-data', Object.keys(asData).length != self.vars('chunk_size'));
|
||||||
|
self.tmp('updatable', true);
|
||||||
|
}, {
|
||||||
|
'project_id': self.vars(['project', 'id']),
|
||||||
|
'chunk': self.tmp('news_chunk')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if(bFirstChunk) { //Delaying important data load
|
||||||
|
if(typeof oUpdateTimer != 'undefined') clearTimeout(oUpdateTimer);
|
||||||
|
oUpdateTimer = setTimeout(function(){updateFeed(true);}, 200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPost(asPost) {
|
||||||
|
var $Post = $('<div>', {'class':'post '+asPost.type});
|
||||||
|
var sRelTime = (self.vars(['project', 'mode'])==self.consts.modes.histo)?asPost.formatted_time.substr(0, 10):asPost.relative_time;
|
||||||
|
var sAbsTime = asPost.formatted_time;
|
||||||
|
var $Body = {};
|
||||||
|
switch(asPost.type)
|
||||||
|
{
|
||||||
|
case 'message':
|
||||||
|
$Body = $('<div>')
|
||||||
|
.append($('<p>').addIcon('fa-compass', true).append('Latitude '+asPost.latitude+', Longitude '+asPost.longitude))
|
||||||
|
.append($('<p>').addIcon('fa-clock-o', true).append(sAbsTime))
|
||||||
|
.append(
|
||||||
|
$('<img>', {'class':'staticmap', title: 'Click pour zoomer', src: getStaticMapUrl(asPost.latitude, asPost.longitude)})
|
||||||
|
.data('lat', asPost.latitude)
|
||||||
|
.data('lng', asPost.longitude)
|
||||||
|
.click(function(){
|
||||||
|
var $This = $(this);
|
||||||
|
var oCenter = L.latLng(parseFloat($This.data('lat')), parseFloat($This.data('lng')));
|
||||||
|
self.tmp('map').setOffsetView(self.tmp('map_offset'), oCenter, 13);
|
||||||
|
})
|
||||||
|
);
|
||||||
|
sClass = 'compass';
|
||||||
|
break;
|
||||||
|
case 'picture':
|
||||||
|
var $Image = $('<img>', {'src': asPost.thumb_path/*, 'style':'transform:rotate('+asPost.rotate+'deg);'*/});
|
||||||
|
$Body = $('<a>', {href: asPost.path, 'data-lightbox': self.consts.title, 'data-title': sAbsTime}).append($Image);
|
||||||
|
sClass = 'image';
|
||||||
|
break;
|
||||||
|
case 'post':
|
||||||
|
$Body = $('<div>')
|
||||||
|
.append($('<p>', {'class':'message'}).text(asPost.content))
|
||||||
|
.append($('<p>', {'class':'signature'}).text('-- '+asPost.formatted_name));
|
||||||
|
sClass = 'comment';
|
||||||
|
break;
|
||||||
|
case 'poster':
|
||||||
|
$Body = $('<p>', {'class':'message'})
|
||||||
|
.append($('<input>', {type:'text', id:'post', name:'post', placeholder:'Message'}))
|
||||||
|
.append($('<input>', {type:'text', id:'name', name:'name', placeholder:'Nom'}))
|
||||||
|
.append($('<button>', {type:'button', id:'submit', name:'submit'}).addIcon('fa-send'));
|
||||||
|
sClass = 'comment';
|
||||||
|
break;
|
||||||
|
case 'loading':
|
||||||
|
$Body = $('<p>', {'class':'spinner'}).addIcon('fa-spin fa-spinner');
|
||||||
|
sClass = 'tasks';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
$Post
|
||||||
|
.append($('<div>', {'class':'header'})
|
||||||
|
.append($('<span>', {'class':'index'}).addIcon('fa-'+sClass))
|
||||||
|
.append($('<span>', {'class':'time', 'title':sAbsTime}).text(sRelTime)))
|
||||||
|
.append($('<div>', {'class':'body'}).append($Body));
|
||||||
|
|
||||||
|
if(asPost.displayed_id) $Post.find('.index').append(' '+asPost.displayed_id);
|
||||||
|
|
||||||
|
return $Post;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getStaticMapUrl(oCenterLat, oCenterLng) {
|
||||||
|
var asParams = [
|
||||||
|
'https://api.mapbox.com/v4/mapbox.satellite', //Domain
|
||||||
|
'url-http%3A%2F%2Fspot.lutran.fr%2Fimages%2Ffootprint.png('+oCenterLng+','+oCenterLat+')', //Marker
|
||||||
|
oCenterLng+','+oCenterLat+',13', //Center + zoom
|
||||||
|
'400x300.png?access_token='+self.vars('mapbox_key') //Image size + access token
|
||||||
|
];
|
||||||
|
|
||||||
|
return asParams.join('/');
|
||||||
|
}
|
||||||
|
</script>
|
||||||
4
script/leaflet.min.js
vendored
4
script/leaflet.min.js
vendored
File diff suppressed because one or more lines are too long
@@ -4,7 +4,7 @@ function Spot(asGlobals)
|
|||||||
this.consts = asGlobals.consts;
|
this.consts = asGlobals.consts;
|
||||||
this.consts.hash_sep = '-';
|
this.consts.hash_sep = '-';
|
||||||
this.consts.title = 'Te Araroa';
|
this.consts.title = 'Te Araroa';
|
||||||
this.consts.default_page = 'messages';
|
this.consts.default_page = 'project';
|
||||||
|
|
||||||
/* Initialization */
|
/* Initialization */
|
||||||
|
|
||||||
@@ -63,11 +63,12 @@ function Spot(asGlobals)
|
|||||||
this.get = function(sAction, fOnSuccess, oVars, fOnError, bProcessIcon)
|
this.get = function(sAction, fOnSuccess, oVars, fOnError, bProcessIcon)
|
||||||
{
|
{
|
||||||
if(!oVars) oVars = {};
|
if(!oVars) oVars = {};
|
||||||
|
fOnError = fOnError || function(textStatus, errorThrown) {console.log(textStatus+' '+errorThrown);};
|
||||||
bProcessIcon = bProcessIcon || false;
|
bProcessIcon = bProcessIcon || false;
|
||||||
//if(bProcessIcon) self.addBufferIcon();
|
//if(bProcessIcon) self.addBufferIcon();
|
||||||
|
|
||||||
oVars['a'] = sAction;
|
oVars['a'] = sAction;
|
||||||
$.ajax(
|
return $.ajax(
|
||||||
{
|
{
|
||||||
url: self.consts.process_page,
|
url: self.consts.process_page,
|
||||||
data: oVars,
|
data: oVars,
|
||||||
@@ -85,8 +86,7 @@ function Spot(asGlobals)
|
|||||||
.fail(function(jqXHR, textStatus, errorThrown)
|
.fail(function(jqXHR, textStatus, errorThrown)
|
||||||
{
|
{
|
||||||
//if(bProcessIcon) self.resetIcon();
|
//if(bProcessIcon) self.resetIcon();
|
||||||
if(!fOnError) console.log(textStatus+' '+errorThrown);
|
fOnError(textStatus, errorThrown);
|
||||||
else fOnError(textStatus);
|
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -10,11 +10,7 @@ class Settings
|
|||||||
const TEXT_ENC = 'UTF-8';
|
const TEXT_ENC = 'UTF-8';
|
||||||
const TIMEZONE = 'Europe/Paris';
|
const TIMEZONE = 'Europe/Paris';
|
||||||
const MAPBOX_KEY = '';
|
const MAPBOX_KEY = '';
|
||||||
|
const IGN_FR_KEY = '';
|
||||||
const LINZ_KEY = '';
|
const LINZ_KEY = '';
|
||||||
const MODE = Spot::MODE_BLOG; //Spot::MODE_HISTO/MODE_BLOG
|
|
||||||
const HISTO_SPAN = array('from'=>'2015-12-29 00:00:00', 'to'=>'2016-02-23 23:59:59');
|
|
||||||
const FEED_ID = ''; //Spot Feed ID
|
|
||||||
const DEBUG = true;
|
const DEBUG = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
?>
|
|
||||||
1270
style/_leaflet.scss
1270
style/_leaflet.scss
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -6,34 +6,37 @@
|
|||||||
@import 'leaflet';
|
@import 'leaflet';
|
||||||
@import 'common';
|
@import 'common';
|
||||||
|
|
||||||
#map {
|
#messages {
|
||||||
position: absolute;
|
|
||||||
left: 0;
|
|
||||||
top: 0;
|
|
||||||
bottom: 0;
|
|
||||||
width: 100%;
|
|
||||||
background: #EEE;
|
|
||||||
|
|
||||||
.loader {
|
#map {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
font-size: 3em;
|
left: 0;
|
||||||
width: 1em;
|
top: 0;
|
||||||
height: 1em;
|
bottom: 0;
|
||||||
top: calc(50% - 0.5em);
|
width: 100%;
|
||||||
left: calc(50% - 0.5em);
|
background: #EEE;
|
||||||
color: #666;
|
|
||||||
@extend .flicker;
|
.loader {
|
||||||
}
|
position: absolute;
|
||||||
|
font-size: 3em;
|
||||||
.track_tooltip {
|
width: 1em;
|
||||||
p {
|
height: 1em;
|
||||||
margin: 0;
|
top: calc(50% - 0.5em);
|
||||||
|
left: calc(50% - 0.5em);
|
||||||
&.name {
|
color: #666;
|
||||||
font-weight: bold;
|
@extend .flicker;
|
||||||
}
|
}
|
||||||
&.description {
|
|
||||||
font-style: italic;
|
.track_tooltip {
|
||||||
|
p {
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
&.name {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
&.description {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -57,11 +60,10 @@
|
|||||||
font-size: 1em;
|
font-size: 1em;
|
||||||
color: #222;
|
color: #222;
|
||||||
|
|
||||||
&.te_araroa {
|
&.main {
|
||||||
border-color: #00ff78;
|
border-color: #00ff78;
|
||||||
}
|
}
|
||||||
|
&.off-track {
|
||||||
&.routeburn {
|
|
||||||
border-color: #0000ff;
|
border-color: #0000ff;
|
||||||
}
|
}
|
||||||
&.hitchhiking {
|
&.hitchhiking {
|
||||||
@@ -73,9 +75,10 @@
|
|||||||
|
|
||||||
#feed {
|
#feed {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 1em;
|
right: 0;
|
||||||
top: 0em;
|
top: 0;
|
||||||
bottom: 0em;
|
bottom: 0;
|
||||||
|
margin-right: 1em;
|
||||||
width: calc(30% - 1em);
|
width: calc(30% - 1em);
|
||||||
z-index: 999;
|
z-index: 999;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user