Completed
Push — master ( 9eb73e...f1c432 )
by Yannick
16:39
created

Elevation::getSec()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 6

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 6
nc 1
nop 1
dl 0
loc 7
rs 9.4285
c 0
b 0
f 0
1
<?php
2
require_once(dirname(__FILE__).'/class.Common.php');
3
require_once(dirname(__FILE__).'/class.Connection.php');
4
/*
5
Copyright 2017 Jozef Môstka <[email protected]>
6
7
Licensed under the Apache License, Version 2.0 (the "License");
8
you may not use this file except in compliance with the License.
9
You may obtain a copy of the License at
10
11
    http://www.apache.org/licenses/LICENSE-2.0
12
13
Unless required by applicable law or agreed to in writing, software
14
distributed under the License is distributed on an "AS IS" BASIS,
15
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
See the License for the specific language governing permissions and
17
limitations under the License.
18
*/
19
/*
20
Modified in 2017 by Ycarus <[email protected]>
21
Original version come from https://github.com/tito10047/hgt-reader
22
*/
23
24
class Elevation {
25
	private $htgFilesDestination;
26
	private $resolution  = -1;
27
	private $measPerDeg;
28
	private $openedFiles = [];
29
30
	public function __construct($htgFilesDestination = '', $resolution = 3) {
31
		if ($htgFilesDestination == '') $htgFilesDestination = dirname(__FILE__).'/../install/tmp/';
32
		$this->htgFilesDestination = $htgFilesDestination;
33
		$this->resolution          = $resolution;
34
		switch ($resolution) {
35
			case 1:
36
				$this->measPerDeg = 3601;
37
				break;
38
			case 3:
39
				$this->measPerDeg = 1201;
40
				break;
41
			default:
42
				throw new \Exception("bad resolution can be only one of 1,3");
43
		}
44
		register_shutdown_function(function () {
45
			$this->closeAllFiles();
46
		});
47
	}
48
49
	public function closeAllFiles() {
50
		foreach ($this->openedFiles as $file) {
51
			fclose($file);
52
		}
53
		$this->openedFiles = [];
54
	}
55
56
	private function getElevationAtPosition($fileName, $row, $column) {
57
		if (!array_key_exists($fileName, $this->openedFiles)) {
58
			if (!file_exists($this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName)) {
59
				throw new \Exception("File '{$fileName}' not exists.");
60
			}
61
			$file = fopen($this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName, "r");
62
			if ($file === false) {
63
				throw new \Exception("Cant open file '{$fileName}' for reading.");
64
			}
65
			$this->openedFiles[$fileName] = $file;
66
		} else {
67
			$file = $this->openedFiles[$fileName];
68
		}
69
70
		if ($row > $this->measPerDeg || $column > $this->measPerDeg) {
71
			//TODO:open next file
72
			throw new \Exception("Not implemented yet");
73
		}
74
		$aRow     = $this->measPerDeg - $row;
75
		$position = ($this->measPerDeg * ($aRow - 1)) + $column;
76
		$position *= 2;
77
		fseek($file, $position);
78
		$short  = fread($file, 2);
79
		$_      = unpack("n*", $short);
80
		$shorts = reset($_);
81
		return $shorts;
82
	}
83
84
	/**
85
	 * @param float $lat
86
	 * @param float $lon
87
	 * @param null  $fName
88
	 *
89
	 * @return mixed
90
	 * @throws \Exception
91
	 */
92
	public function getElevation($lat, $lon, &$fName = null) {
93
		if ($this->resolution == -1) {
94
			throw new \Exception("use HgtReader::init(ASSETS_HGT . DIRECTORY_SEPARATOR, 3);");
95
		}
96
		$N      = $this->getDeg($lat, 2);
97
		$E      = $this->getDeg($lon, 3);
98
		$fName  = "N{$N}E{$E}.hgt";
99
		
100
		$latSec = $this->getSec($lat);
101
		$lonSec = $this->getSec($lon);
102
103
		$Xn = round($latSec / $this->resolution, 3);
104
		$Yn = round($lonSec / $this->resolution, 3);
105
106
		$a1 = round($Xn);
107
		$a2 = round($Yn);
108
109
		if ($Xn <= $a1 && $Yn <= $a2) {
110
			$b1 = $a1 - 1;
111
			$b2 = $a2;
112
			$c1 = $a1;
113
			$c2 = $a2 - 1;
114
		} else if ($Xn >= $a1 && $Yn >= $a2) {
115
			$b1 = $a1 + 1;
116
			$b2 = $a2;
117
			$c1 = $a1;
118
			$c2 = $a2 + 1;
119
		} else if ($Xn > $a1 && $Yn < $a2) {
120
			$b1 = $a1;
121
			$b2 = $a2 - 1;
122
			$c1 = $a1 + 1;
123
			$c2 = $a2;
124
		} else if ($Xn < $a1 && $Yn > $a2) {
125
			$b1 = $a1 - 1;
126
			$b2 = $a2;
127
			$c1 = $a1;
128
			$c2 = $a2 + 1;
129
		} else {
130
			throw new \Exception("{$Xn}:{$Yn}");
131
		}
132
		$a3 = $this->getElevationAtPosition($fName, $a1, $a2);
133
		$b3 = $this->getElevationAtPosition($fName, $b1, $b2);
134
		$c3 = $this->getElevationAtPosition($fName, $c1, $c2);
135
136
		$n1 = ($c2 - $a2) * ($b3 - $a3) - ($c3 - $a3) * ($b2 - $a2);
137
		$n2 = ($c3 - $a3) * ($b1 - $a1) - ($c1 - $a1) * ($b3 - $a3);
138
		$n3 = ($c1 - $a1) * ($b2 - $a2) - ($c2 - $a2) * ($b1 - $a1);
139
140
		$d  = -$n1 * $a1 - $n2 * $a2 - $n3 * $a3;
141
		$zN = (-$n1 * $Xn - $n2 * $Yn - $d) / $n3;
142
143
		return $zN;
144
	}
145
146
	private function getDeg($deg, $numPrefix) {
147
		$deg = abs($deg);
148
		$d   = floor($deg);     // round degrees
149
		if ($numPrefix >= 3) {
150
			if ($d < 100) {
151
				$d = '0' . $d;
152
			}
153
		} // pad with leading zeros
154
		if ($d < 10) {
155
			$d = '0' . $d;
156
		}
157
		return $d;
158
	}
159
160
	private function getSec($deg) {
161
		$deg = abs($deg);
162
		$sec = round($deg * 3600, 4);
163
		$m   = fmod(floor($sec / 60), 60);
164
		$s   = round(fmod($sec, 60), 4);
165
		return ($m * 60) + $s;
166
	}
167
168
	public function download($lat,$lon, $debug = false) {
169
		$N      = $this->getDeg($lat, 2);
170
		$E      = $this->getDeg($lon, 3);
171
		$fileName  = "N{$N}E{$E}.hgt";
172
		if (!file_exists($this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName)) {
173
			$Common = new Common();
174
			if ($debug) echo 'Downloading '.$fileName.'.gz ...';
175
			$Common->download('https://s3.amazonaws.com/elevation-tiles-prod/skadi/N'.$N.'/'.$fileName.'.gz',$this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName . '.gz');
176
			if (!file_exists($this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName . '.gz')) {
177
				if ($debug) echo "File '{$fileName}.gz' not exists.";
178
				return false;
179
			}
180
			if ($debug) echo 'Done'."\n";
181
			if ($debug) echo 'Decompress '.$fileName.' ....';
182
			$Common->gunzip($this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName . '.gz',$this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName);
183
			if ($debug) echo 'Done'."\n";
184
			unlink($this->htgFilesDestination . DIRECTORY_SEPARATOR . $fileName . '.gz');
185
		}
186
		return true;
187
	}
188
	
189
	public function downloadNeeded() {
190
		$Connection = new Connection();
191
		$db = $Connection->db;
192
		$query = 'SELECT latitude, longitude FROM spotter_output WHERE latitude <> 0 AND longitude <> 0 ORDER BY date DESC LIMIT 100';
193
		$query_values = array();
194
		try {
195
			$sth = $db->prepare($query);
1 ignored issue
show
Bug introduced by
The method prepare cannot be called on $db (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
196
			$sth->execute($query_values);
197
		} catch(PDOException $e) {
198
			return "error : ".$e->getMessage();
199
		}
200
		while ($data = $sth->fetch(PDO::FETCH_ASSOC)) {
201
			$this->download($data['latitude'],$data['longitude'],true);
202
		}
203
		$query = 'SELECT latitude, longitude FROM tracker_output WHERE latitude <> 0 AND longitude <> 0 ORDER BY date DESC LIMIT 100';
204
		$query_values = array();
205
		try {
206
			$sth = $db->prepare($query);
1 ignored issue
show
Bug introduced by
The method prepare cannot be called on $db (of type null).

Methods can only be called on objects. This check looks for methods being called on variables that have been inferred to never be objects.

Loading history...
207
			$sth->execute($query_values);
208
		} catch(PDOException $e) {
209
			return "error : ".$e->getMessage();
210
		}
211
		while ($data = $sth->fetch(PDO::FETCH_ASSOC)) {
212
			$this->download($data['latitude'],$data['longitude'],true);
213
		}
214
	}
215
}
216
/*
217
$lat = 46.3870;
218
$lon = 5.2941;
219
$Elevation = new Elevation();
220
$Elevation->download($lat,$lon);
221
echo($Elevation->getElevation($lat,$lon));
222
*/
223
/*
224
$Elevation = new Elevation();
225
echo $Elevation->downloadNeeded();
226
*/