pano.js 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354
  1. if (ref_points == undefined) var ref_points = new Array();
  2. if (image_loop == undefined) var image_loop = false;
  3. var csrf_token = Cookies.get('csrftoken');
  4. var debug_mode = false;
  5. var canvas;
  6. var cntext;
  7. var point_list = new Array();
  8. var zoom = 0;
  9. var zooms = new Array();
  10. var prev_zm;
  11. var zm;
  12. var tile = {width:256, height:256};
  13. var ntiles = {x:228, y:9};
  14. var border_width = 2;
  15. var imageObj = new Array();
  16. // minimum and maximum azimuth
  17. var alpha_domain = {start:0, end:360};
  18. var fingr = 0; // mémorisation de lécart entre doigts;
  19. var last = {x:0,y:0};
  20. var shift = {x:0,y:0};
  21. var mouse = {x:0,y:0};
  22. var speed = {x:0,y:0};
  23. var canvas_pos = {x:0,y:0};
  24. var tmt;
  25. var is_located = false;
  26. var point_colors = {
  27. 'pano_point' : '255,128,128', // red
  28. 'ref_point' : '128,128,255', // blue
  29. 'loc_point' : '128,255,128', // green
  30. 'loc_point_other' : '128,255,128', // green
  31. 'loc_point_subscriber' : '255,128,255', // purple
  32. 'loc_point_waiting' : '255,196,128', // orange
  33. 'temporary' : '255,255,128', // yellow
  34. 'unlocated' : '255,255,255' // white
  35. };
  36. var map_never_drawn = true; //Pour empecher d'enlever des Layers inexistants
  37. var map; // minimap object
  38. var viewField; // cone drawn on the minimap
  39. var viewDirection; // blue line drawn on the minimap
  40. function getXMLHttpRequest() {
  41. var xhr = null;
  42. if (window.XMLHttpRequest || window.ActiveXObject) {
  43. if (window.ActiveXObject) {
  44. try {
  45. xhr = new ActiveXObject("Msxml2.XMLHTTP");
  46. } catch(e) {
  47. xhr = new ActiveXObject("Microsoft.XMLHTTP");
  48. }
  49. } else {
  50. xhr = new XMLHttpRequest();
  51. }
  52. } else {
  53. alert("Votre navigateur ne supporte pas l'objet XMLHTTPRequest...");
  54. return null;
  55. }
  56. return xhr;
  57. }
  58. function nmodulo(val, div) { // pour obtenir un modulo dans l'espace des nombres naturels N.
  59. return Math.floor((val%div+div)%div); // il y a peut être plus simple, mais en attendant ....
  60. }
  61. function fmodulo(val, div) { // pour obtenir un modulo dans l'espace des nombres réels positifs.
  62. return (val%div+div)%div; // il y a peut être plus simple, mais en attendant ....
  63. }
  64. function distort_canvas(p, x, y) {
  65. if (p == 0) distort = 0;
  66. else {
  67. cntext.save();
  68. distort++;
  69. cntext.clearRect(0, 0, canvas.width, 2*canvas.height);
  70. var ratio = (canvas.width-2*distort)/canvas.width;
  71. var shift = canvas.height/2*(1-ratio);
  72. cntext.scale(1, ratio);
  73. if (p == 1) cntext.translate(0, 0);
  74. else if (p == -1) cntext.translate(0, 0);
  75. draw_image(x, y);
  76. cntext.restore();
  77. }
  78. }
  79. function draw_image(ox, oy) {
  80. var ref_vals = {x:last.x, y:last.y, zoom:zoom};
  81. ox = nmodulo(ox-canvas.width/2, zm.im.width); // pour placer l'origine au centre du canvas
  82. oy = Math.floor(oy-canvas.height/2); // pas de rebouclage vertical
  83. cntext.clearRect(0, 0, canvas.width, canvas.height);
  84. cntext.fillStyle = "rgba(128,128,128,0.8)";
  85. if (canvas.height > zm.im.height) {
  86. var fy = Math.floor((oy+canvas.height/2-zm.im.height/2)/(tile.height*zm.ntiles.y))*zm.ntiles.y;
  87. if (fy < 0) fy = 0;
  88. var ly = fy + zm.ntiles.y;
  89. } else {
  90. var fy = Math.floor(oy/tile.height);
  91. var ly = Math.floor((oy+canvas.height+tile.height-1)/tile.height+1);
  92. if (fy < 0) fy = 0;
  93. if (ly > zm.ntiles.y) ly = zm.ntiles.y;
  94. }
  95. for (var j=fy; j<ly; j++) {
  96. var delta_y = (Math.floor(j/zm.ntiles.y) - Math.floor(fy/zm.ntiles.y)) * (tile.height - zm.last_tile.height);
  97. var dy = j*tile.height - oy - delta_y;
  98. var ny = j%ntiles.y;
  99. var wy = zm.tile.width;
  100. if (ny == zm.ntiles.y - 1) wy = zm.last_tile.height;
  101. var cpx = 0;
  102. var i = 0;
  103. var Nx = zm.ntiles.x;
  104. while (cpx < ox+canvas.width) {
  105. var cur_width = zm.tile.width;
  106. if (i%Nx == zm.ntiles.x-1) cur_width = zm.last_tile.width;
  107. if (cpx >= ox-cur_width) {
  108. var nx = i%Nx;
  109. var idx = nx+'-'+ny+'-'+ref_vals.zoom;
  110. if (imageObj[idx] && imageObj[idx].complete) {
  111. draw_tile(idx, cpx-ox, dy); // l'image est déja en mémoire, on force le dessin sans attendre.
  112. } else {
  113. var fname = get_file_name(nx, ny, ref_vals.zoom);
  114. imageObj[idx] = new Image();
  115. imageObj[idx].src = fname;
  116. var ts = zm.get_tile_size(nx, ny);
  117. cntext.fillRect(cpx-ox, dy, ts.width, ts.height);
  118. imageObj[idx].addEventListener('load', (function(ref, idx, dx, dy, ox, oy, ts) {
  119. return function() { // closure nécéssaire pour gestion assynchronisme !!!
  120. draw_tile_del(ref, idx, dx, dy, ox, oy, ts.width, ts.height);
  121. };
  122. })(ref_vals, idx, cpx-ox, dy, ox, oy, ts), false);
  123. }
  124. // load_image(zoom, nx, ny, shx, shy, cpx-ox, dy, ox, oy);
  125. }
  126. cpx += cur_width;
  127. i++;
  128. }
  129. }
  130. drawDecorations(ox, oy);
  131. var cap_ele = zm.get_cap_ele(last.x, zm.im.height/2-last.y);
  132. angle_control.value = cap_ele.cap.toFixed(2);
  133. elvtn_control.value = cap_ele.ele.toFixed(2);
  134. update_url();
  135. // draw minimap
  136. if (Object.keys(ref_points).length>1){
  137. // Draw the mijimap only if there are at least 2 reference points (so the size is known)
  138. if (typeof panorama_lat !== 'undefined') {
  139. if (map_never_drawn ) { load_map() }
  140. update_map();
  141. } else {
  142. // hack: It updates well when the 2nd points is pointed, but the map
  143. // failed to load due to the missing JS variables linked to the panorama
  144. // (head of view.html), so reload the page first.
  145. location.reload();
  146. };
  147. $('#expandmap').css({'visibility': 'visible'})
  148. } else {
  149. // remove the expandmap button
  150. $('#expandmap').css({'visibility': 'hidden'})
  151. }
  152. }
  153. function draw_tile_del(ref, idx, tx, ty, ox, oy, twidth, theight) {
  154. if (ref.zoom == zoom && ref.x == last.x && ref.y == last.y) {
  155. draw_tile(idx, tx, ty);
  156. drawDecorations(ox, oy, tx, ty, twidth, theight);
  157. }
  158. }
  159. function draw_tile(idx, ox, oy) {
  160. var img = imageObj[idx];
  161. cntext.drawImage(img, ox, oy);
  162. }
  163. /** Draws the colored circles and the central line
  164. */
  165. function drawDecorations(ox, oy, tx, ty, twidth, theight) {
  166. if (twidth) {
  167. cntext.save();
  168. cntext.beginPath();
  169. cntext.rect(tx, ty, twidth, theight);
  170. cntext.clip();
  171. }
  172. var wgrd = zm.im.width/360;
  173. var od = ((ox+canvas.width/2)/wgrd)%360;
  174. var el = (zm.im.height/2 - (oy+canvas.height/2))/wgrd;
  175. // draw a vertical blue line with the central dot
  176. // the dot is centered on (ox, oy) = (canvas.width/2, canvas.width/2)
  177. var line_width = 3;
  178. cntext.fillStyle = "rgba(43, 130, 203, 0.7)";
  179. cntext.strokeStyle = "yellow";
  180. cntext.lineWidth = 2;
  181. cntext.fillRect(canvas.width/2-line_width/2, 0, line_width, canvas.height);
  182. cntext.strokeRect(canvas.width/2-line_width, canvas.height/2-line_width, line_width*2, line_width*2);
  183. for(var i = 0; i < zm.pt_list.length; i++) {
  184. if (zm.pt_list[i]['type'] != 'unlocated') {
  185. cntext.fillStyle = 'rgba('+point_colors[zm.pt_list[i]['type']]+',0.5)';
  186. var cx = nmodulo(zm.pt_list[i]['xc'] - ox, zm.im.width);
  187. var cy = zm.pt_list[i]['yc'] - oy;
  188. var cy_ground = zm.pt_list[i]['yc_ground'] - oy;
  189. cntext.beginPath();
  190. cntext.arc(cx, cy, 20, 0, 2*Math.PI, true);
  191. cntext.fill();
  192. // Draw line that shows the height of the building
  193. cntext.fillStyle = "rgba(0,0,255,0.45)";
  194. cntext.fillRect(cx-1, cy, 3, cy_ground - cy);
  195. }
  196. }
  197. if (twidth) {
  198. cntext.restore();
  199. }
  200. }
  201. function insert_drawn_point(lat, lon, alt) {
  202. var rt = 6371; // Rayon de la terre
  203. var pt_alt = document.getElementById('pos_alt').childNodes[0].nodeValue;
  204. var pt_lat = document.getElementById('pos_lat').childNodes[0].nodeValue;
  205. var pt_lon = document.getElementById('pos_lon').childNodes[0].nodeValue;
  206. var alt1 = pt_alt;
  207. var lat1 = pt_lat*Math.PI/180;
  208. var lon1 = pt_lon*Math.PI/180;
  209. var alt2 = alt;
  210. var lat2 = lat*Math.PI/180;
  211. var lon2 = lon*Math.PI/180;
  212. var dLat = lat2-lat1;
  213. var dLon = lon2-lon1;
  214. var a = Math.sin(dLat/2)*Math.sin(dLat/2) + Math.sin(dLon/2)*Math.sin(dLon/2)*Math.cos(lat1)*Math.cos(lat2); //
  215. var angle = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
  216. var d = angle*rt; // distance du point en Kms
  217. var y = Math.sin(dLon) * Math.cos(lat2);
  218. var x = Math.cos(lat1)*Math.sin(lat2) - Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);
  219. var cap = Math.atan2(y,x); // cap pour atteindre le point en radians
  220. var e = Math.atan2((alt2 - alt1)/1000 - d*d/(2*rt),d); // angle de l'élévation en radians
  221. return {d:d, cap:cap*180/Math.PI, ele:e*180/Math.PI}; // les résultats sont en degrés
  222. }
  223. function localate_point() {
  224. var lat = document.getElementById("loca_latitude").value;
  225. var lon = document.getElementById("loca_longitude").value;
  226. var alt = document.getElementById("loca_altitude").value;
  227. if (lat == '' || isNaN(lat) || lat <= -90 || lat > 90) {
  228. alert("La latitude "+lat+"n'est pas correcte");
  229. return;
  230. }
  231. if (lat == '' || isNaN(lon) || lon <= -180 || lon > 180) {
  232. alert("La longitude "+lon+"n'est pas correcte");
  233. return;
  234. }
  235. if (lat == '' || isNaN(alt) || alt < -400 || alt > 10000000) {
  236. alert("l'altitude "+alt+"n'est pas correcte");
  237. return;
  238. }
  239. var opt_ced = new Array();
  240. opt_dce = insert_drawn_point(lat, lon, alt);
  241. display_temp(opt_dce.d, opt_dce.cap, opt_dce.ele);
  242. }
  243. function display_temp(d,cap,ele) {
  244. point_list[point_list.length] = new Array("point temporaire", d, d, cap, ele, ele, "temporary");
  245. reset_zooms();
  246. putImage(last.x, last.y);
  247. }
  248. function arrayUnset(array, value){
  249. array.splice(array.indexOf(value), 1);
  250. }
  251. function erase_point() {
  252. for (var i=0; i<point_list.length; i++) {
  253. if(point_list[i][0] == "point temporaire"){
  254. arrayUnset(point_list,point_list[i]);
  255. loop = erase_point();
  256. }
  257. }
  258. reset_zooms();
  259. putImage(last.x, last.y);
  260. }
  261. /** Returns a 3-width zero-padded version of an int
  262. * ex: 3 -> "003"
  263. */
  264. function zero_pad(number) {
  265. var temp = number.toString(10);
  266. while (temp.length < 3) {
  267. temp = '0' + temp;
  268. }
  269. return temp;
  270. }
  271. function get_file_name(x, y, z) { // recherche du fichier correspondant au zoom et à la position
  272. return img_prefix+'/'+zero_pad(z)+'-'+zero_pad(x)+'-'+zero_pad(y)+'.jpg';
  273. }
  274. function get_base_name() {
  275. /**
  276. * @returns the base name, which is the name (not path) of the folder where
  277. * the tiles are.
  278. */
  279. return img_prefix.split('/').reverse()[0];
  280. }
  281. function keys(key) {
  282. hide_links();
  283. evt = key || event;
  284. //evt.preventDefault();
  285. //evt.stopPropagation();
  286. if (!key) {
  287. key = window.event;
  288. key.which = key.keyCode;
  289. }
  290. //alert(key);
  291. //if (!evt.shiftKey) return;
  292. switch (key.which) {
  293. case 36: // home
  294. case 35: // end
  295. angle_control = document.getElementById('angle_ctrl');
  296. angle_control.value = parseFloat(angle_control.value) + 180;
  297. change_angle();
  298. case 39: // left
  299. putImage(last.x+40, last.y);
  300. break;
  301. case 40: // up
  302. putImage(last.x, last.y+20);
  303. break;
  304. case 37: // right
  305. putImage(last.x-40, last.y);
  306. break;
  307. case 38: // down
  308. putImage(last.x, last.y-20);
  309. break;
  310. case 33: // pageup
  311. zoom_control.value--;
  312. change_zoom()
  313. break;
  314. case 34: // pagedown
  315. zoom_control.value++;
  316. change_zoom()
  317. break;
  318. default:
  319. // alert(key.which)
  320. break;
  321. }
  322. }
  323. function onImageClick(e) {
  324. hide_contextmenu();
  325. var index = {};
  326. if (e.touches && e.touches.length == 2) {
  327. e.preventDefault();
  328. fingr = Math.sqrt((e.touches[0].clientX - e.touches[1].clientX)^2 +
  329. (e.touches[0].clientY - e.touches[1].clientY)^2);
  330. } else {
  331. if (e.touches) {
  332. e.preventDefault();
  333. index.x = e.changedTouches[0].clientX;
  334. index.y = e.changedTouches[0].clientY;
  335. } else {
  336. index.x = e.pageX;
  337. index.y = e.pageY;
  338. }
  339. shift.x = last.x;
  340. shift.y = last.y;
  341. speed.x = 0;
  342. speed.y = 0;
  343. mouse.x = index.x;
  344. mouse.y = index.y;
  345. }
  346. clearTimeout(tmt); //arrêt de l'éffet eventuel d'amorti en cours.
  347. canvas.addEventListener('mousemove', stickImage, false);
  348. canvas.addEventListener('touchmove', stickImage, false);
  349. canvas.addEventListener('mouseup', launchImage, false);
  350. canvas.addEventListener('touchend', launchImage, false);
  351. //canvas.addEventListener('mouseout', launchImage, false);
  352. canvas.style.cursor='move';
  353. //document.onmousemove = stickImage;
  354. //document.onmouseup = launchImage;
  355. hide_links();
  356. }
  357. function stickImage(e) {
  358. var index = {};
  359. if (e.changedTouches && e.changedTouches.length == 2) {
  360. e.preventDefault();
  361. // cas du zoom à 2 doigts
  362. var nfingr = Math.sqrt((e.changedTouches[0].clientX - e.changedTouches[1].clientX)^2 +
  363. (e.changedTouches[0].clientY - e.changedTouches[1].clientY)^2);
  364. var evt = {}
  365. evt.pageX = (e.changedTouches[0].clientX + e.changedTouches[1].clientX)/2;
  366. evt.pageY = (e.changedTouches[0].clientY + e.changedTouches[1].clientY)/2;
  367. if (fingr > nfingr*2 || fingr < nfingr/2) {
  368. evt.wheelDelta = fingr - nfingr;
  369. fingr = nfingr;
  370. return wheel_zoom(evt);
  371. } else {
  372. return;
  373. }
  374. }
  375. if (e.touches) {
  376. e.preventDefault();
  377. index.x = e.changedTouches[0].clientX;
  378. index.y = e.changedTouches[0].clientY;
  379. } else {
  380. index.x = e.pageX;
  381. index.y = e.pageY;
  382. }
  383. var xs = mouse.x - index.x + shift.x;
  384. var ys = mouse.y - index.y + shift.y;
  385. speed.x = xs - last.x; //mémorisation des vitesses horizontales
  386. speed.y = ys - last.y; //et verticales lors de ce déplacement
  387. putImage(xs, ys);
  388. }
  389. function launchImage(e) {
  390. var index = {};
  391. if (e.touches) {
  392. e.preventDefault();
  393. index.x = e.changedTouches[0].clientX;
  394. index.y = e.changedTouches[0].clientY;
  395. } else {
  396. index.x = e.pageX;
  397. index.y = e.pageY;
  398. }
  399. distort_canvas(0);
  400. canvas.removeEventListener('mousemove', stickImage, false);
  401. canvas.removeEventListener('touchmove', stickImage, false);
  402. //document.onmousemove = null;
  403. shift.x = index.x - mouse.x + shift.x;
  404. shift.y = index.y - mouse.y + shift.y;
  405. tmt = setTimeout(inertialImage, 100);
  406. }
  407. function putImage(x, y) { // est destiné à permettre l'effet d'amortissement par la mémorisation de la position courante.
  408. if (!zm.is_updated) return;
  409. if (x >= zm.im.width) { // rebouclage horizontal
  410. shift.x -= zm.im.width;
  411. x -= zm.im.width;
  412. } else if (x < 0) {
  413. shift.x += zm.im.width;
  414. x += zm.im.width;
  415. }
  416. if (y >= zm.im.height) { // pas de rebouclage vertical mais blocage
  417. //distort_canvas(1, x, y);
  418. shift.y = zm.im.height-1;
  419. y = zm.im.height-1;
  420. } else if (y < 0) {
  421. //distort_canvas(-1, x, y);
  422. shift.y = 0;
  423. y = 0;
  424. }
  425. last.x = x;
  426. last.y = y;
  427. draw_image(x, y);
  428. }
  429. function inertialImage() {
  430. speed.x *= 0.9;
  431. speed.y *= 0.9;
  432. if (Math.abs(speed.x) > 2 || Math.abs(speed.y) > 2) {
  433. putImage(last.x+speed.x, last.y+speed.y);
  434. tmt = setTimeout(inertialImage, 100);
  435. } else {
  436. show_links();
  437. }
  438. }
  439. function tri_ref_points(v1, v2) {
  440. return v1['x'] - v2['x'];
  441. }
  442. function tzoom(zv) {
  443. this.value = zv;
  444. this.ntiles = {x:0,y:0};
  445. this.tile = {width:0,height:0};
  446. this.last_tile = {width:0,height:0};
  447. this.max_tile = {width:0,height:0};
  448. this.im = {width:0,height:0};
  449. this.is_updated = false;
  450. this.refresh = function() {
  451. this.im.visible_width = this.tile.width*(this.ntiles.x-1)+this.last_tile.width;
  452. this.is_updated = true;
  453. this.im.width = this.im.visible_width;
  454. this.im.height = this.tile.height*(this.ntiles.y-1)+this.last_tile.height;
  455. if (this.last_tile.width > this.tile.width) {
  456. this.max_tile.width = this.im.last_tile.width;
  457. } else {
  458. this.max_tile.width = this.tile.width;
  459. }
  460. if (this.last_tile.height > this.tile.height) {
  461. this.max_tile.height = this.im.last_tile.height;
  462. } else {
  463. this.max_tile.height = this.tile.height;
  464. }
  465. var ord_pts = new Array();
  466. for(var label in ref_points) {
  467. ord_pts.push(ref_points[label]);
  468. }
  469. ord_pts = ord_pts.sort(tri_ref_points);
  470. is_located = (ord_pts.length > 1)
  471. || image_loop && (ord_pts.length > 0);
  472. alpha_domain = {start:0, end:360};
  473. this.pixel_y_ratio = this.im.width/360;
  474. if (is_located) {
  475. this.ref_pixels = new Array;
  476. this.ref_pixels[0] = new Array(); // Attention il faut compter un intervalle de plus !
  477. for (var i=0; i < ord_pts.length; i++) { // premier parcours pour les paramètres cap/x
  478. this.ref_pixels[i+1] = new Array();
  479. this.ref_pixels[i+1].x = Math.floor(ord_pts[i].x*this.im.width);
  480. this.ref_pixels[i+1].cap = fmodulo(ord_pts[i].cap, 360);
  481. if (i != ord_pts.length-1) {
  482. this.ref_pixels[i+1].ratio_x = (ord_pts[i+1].x - ord_pts[i].x) /
  483. fmodulo(ord_pts[i+1].cap - ord_pts[i].cap, 360)*this.im.width;
  484. }
  485. }
  486. if (image_loop == true) {
  487. var dpix = this.im.width;
  488. var dangle = 360;
  489. if (ord_pts.length > 1) {
  490. dpix = this.im.width - this.ref_pixels[this.ref_pixels.length-1].x + this.ref_pixels[1].x;
  491. dangle = fmodulo(this.ref_pixels[1].cap - this.ref_pixels[this.ref_pixels.length-1].cap, 360);
  492. }
  493. this.ref_pixels[0].ratio_x = dpix/dangle;
  494. this.ref_pixels[ord_pts.length].ratio_x = this.ref_pixels[0].ratio_x;
  495. dpix = this.im.width - this.ref_pixels[ord_pts.length].x;
  496. this.ref_pixels[0].cap = fmodulo(this.ref_pixels[ord_pts.length].cap + dpix / this.ref_pixels[0].ratio_x, 360);
  497. } else {
  498. this.ref_pixels[0].ratio_x = this.ref_pixels[1].ratio_x;
  499. this.ref_pixels[ord_pts.length].ratio_x = this.ref_pixels[ord_pts.length-1].ratio_x;
  500. this.ref_pixels[0].cap = fmodulo(this.ref_pixels[1].cap - this.ref_pixels[1].x / this.ref_pixels[1].ratio_x, 360);
  501. alpha_domain.start = this.ref_pixels[0].cap;
  502. alpha_domain.end = fmodulo(this.ref_pixels[ord_pts.length].cap+(this.im.width-this.ref_pixels[ord_pts.length].x)/this.ref_pixels[ord_pts.length].ratio_x, 360);
  503. this.pixel_y_ratio = this.im.width/fmodulo(alpha_domain.end-alpha_domain.start, 360);
  504. }
  505. this.ref_pixels[0].x = 0;
  506. for (var i=0; i < ord_pts.length; i++) { // second parcours pour les paramètres elevation/y
  507. this.ref_pixels[i+1].shift_y = Math.floor(this.pixel_y_ratio*ord_pts[i].ele - ord_pts[i].y*this.im.height);
  508. if (i != ord_pts.length-1) {
  509. var next_shift = Math.floor(this.pixel_y_ratio*ord_pts[i+1].ele - ord_pts[i+1].y*this.im.height);
  510. this.ref_pixels[i+1].dshft_y = (next_shift - this.ref_pixels[i+1].shift_y)/(this.ref_pixels[i+2].x - this.ref_pixels[i+1].x);
  511. }
  512. }
  513. if (image_loop == true) {
  514. var dpix = this.im.width;
  515. var delt = 0;
  516. if (ord_pts.length > 1) {
  517. dpix = this.im.width - this.ref_pixels[this.ref_pixels.length-1].x + this.ref_pixels[1].x;
  518. delt = this.ref_pixels[this.ref_pixels.length-1].shift_y - this.ref_pixels[1].shift_y;
  519. }
  520. this.ref_pixels[0].dshft_y = -delt/dpix;
  521. this.ref_pixels[ord_pts.length].dshft_y = this.ref_pixels[0].dshft_y;
  522. dpix = this.im.width - this.ref_pixels[ord_pts.length].x;
  523. this.ref_pixels[0].shift_y = this.ref_pixels[ord_pts.length].shift_y + dpix*this.ref_pixels[0].dshft_y;
  524. } else {
  525. this.ref_pixels[0].shift_y = this.ref_pixels[1].shift_y;
  526. this.ref_pixels[0].dshft_y = 0;
  527. this.ref_pixels[ord_pts.length].dshft_y = 0;
  528. }
  529. }
  530. this.pt_list = new Array();
  531. for (var i=0; i<point_list.length; i++) {
  532. var lbl = point_list[i][0];
  533. var dst = point_list[i][1];
  534. var dst_human = point_list[i][2];
  535. var cap = point_list[i][3];
  536. var ele = point_list[i][4];
  537. var ele_ground = point_list[i][5];
  538. var lnk = point_list[i][6];
  539. var url = point_list[i][7];
  540. var typ = 'unlocated';
  541. var rxy = this.get_pos_xy(cap, ele);
  542. var rxy_ground = this.get_pos_xy(cap, ele_ground);
  543. var is_visible = (
  544. fmodulo(cap - alpha_domain.start, 360)
  545. <=
  546. fmodulo(alpha_domain.end -
  547. alpha_domain.start -0.0001, 360)+0.0001
  548. && is_located);
  549. this.pt_list[i] = new Array();
  550. if (ref_points[lbl] != undefined && lnk == '') {
  551. typ = 'ref_point';
  552. if (!is_located) {
  553. rxy = {
  554. x:ref_points[lbl].x*this.im.width,
  555. y:ref_points[lbl].y*this.im.height
  556. };
  557. }
  558. } else if(lnk == '' && is_visible && lbl != 'point temporaire') {
  559. typ = 'loc_point';
  560. }else if(is_visible && lbl =='point temporaire') {
  561. typ = 'temporary';
  562. } else if(is_visible) {
  563. typ = 'pano_point';
  564. }
  565. this.pt_list[i]['type'] = typ;
  566. this.pt_list[i]['cap'] = cap;
  567. this.pt_list[i]['ele'] = ele;
  568. this.pt_list[i]['ele_ground'] = ele_ground;
  569. this.pt_list[i]['dist'] = dst;
  570. this.pt_list[i]['dist_human'] = dst_human;
  571. this.pt_list[i]['label'] = lbl;
  572. this.pt_list[i]['lnk'] = lnk;
  573. this.pt_list[i]['url'] = url;
  574. this.pt_list[i]['xc'] = rxy.x;
  575. this.pt_list[i]['yc'] = Math.floor(this.im.height/2 - rxy.y);
  576. this.pt_list[i]['yc_ground'] = Math.floor(this.im.height/2 - rxy_ground.y);
  577. }
  578. },
  579. this.get_tile_size = function(nx, ny) {
  580. var res = {width:0, height:0};
  581. if (nx == this.ntiles.x-1) res.width = this.last_tile.width;
  582. else res.width = this.tile.width;
  583. if (ny == this.ntiles.y-1) res.height = this.last_tile.height;
  584. else res.height = this.tile.height;
  585. return res;
  586. }
  587. this.get_cap_ele = function(px, py) { // recherche d'un cap et d'une élévation à partir d'un pixel X,Y.
  588. if (is_located) {
  589. for (var i=0; i < this.ref_pixels.length; i++) {
  590. if (i == this.ref_pixels.length - 1 || px < this.ref_pixels[i+1].x) {
  591. var dpix = px-this.ref_pixels[i].x;
  592. var cp = fmodulo(this.ref_pixels[i].cap + dpix/this.ref_pixels[i].ratio_x, 360);
  593. var el = (py+this.ref_pixels[i].shift_y+this.ref_pixels[i].dshft_y*dpix)/this.pixel_y_ratio;
  594. return {cap:cp, ele:el};
  595. }
  596. }
  597. } else {
  598. var cp = 360*px/this.im.width;
  599. var el = 360*py/this.im.height;
  600. return {cap:cp, ele:el};
  601. }
  602. }
  603. this.get_pos_xy = function(cap, ele) { // recherche des coordonnées pixel à partir d'un cap et d'une élévation.
  604. if (is_located) {
  605. var dcap = fmodulo(cap-this.ref_pixels[0].cap, 360);
  606. for (var i=0; i < this.ref_pixels.length; i++) {
  607. if (i == this.ref_pixels.length - 1 || dcap < fmodulo(this.ref_pixels[i+1].cap-this.ref_pixels[0].cap, 360)) {
  608. var px = this.ref_pixels[i].x + this.ref_pixels[i].ratio_x*fmodulo(cap - this.ref_pixels[i].cap, 360);
  609. var dpix = px-this.ref_pixels[i].x;
  610. var py = this.pixel_y_ratio*ele - this.ref_pixels[i].shift_y - this.ref_pixels[i].dshft_y*dpix;
  611. if (image_loop || (dcap < fmodulo(alpha_domain.end - alpha_domain.start, 360)))
  612. // Position is inside the view
  613. return {x: px, y: py};
  614. else {
  615. // Position is outside the view, find out which edge is closest
  616. if (fmodulo(alpha_domain.start - cap, 360) < fmodulo(cap - alpha_domain.end, 360))
  617. // Left edge
  618. return {x: 0, y: py};
  619. else
  620. // Right edge
  621. return {x: image_width - 1, y: py};
  622. }
  623. }
  624. }
  625. } else {
  626. var px = fmodulo(cap, 360)/360*this.im.width;
  627. var py = ele/360*this.im.height;
  628. return {x:px, y:py};
  629. }
  630. }
  631. }
  632. function reset_zooms () {
  633. for(i=0; i<zooms.length; i++) zooms[i].is_updated = false;
  634. zm.refresh();
  635. }
  636. function wheel_zoom (event) {
  637. var zshift = {x:0, y:0};
  638. if (event.pageX != undefined && event.pageX != undefined) {
  639. zshift.x = event.pageX-canvas.width/2-canvas_pos.x;
  640. zshift.y = event.pageY-canvas.height/2-canvas_pos.y;
  641. }
  642. //event.preventDefault();
  643. var delta = (event.wheelDelta || -event.detail);
  644. if (delta < 0 && zoom_control.value < zoom_control.max) {
  645. zoom_control.value++;
  646. change_zoom(zshift.x, zshift.y);
  647. } else if (delta > 0 && zoom_control.value > zoom_control.min) {
  648. zoom_control.value--;
  649. change_zoom(zshift.x, zshift.y);
  650. }
  651. }
  652. function change_zoom(shx, shy) {
  653. var zoom_control = document.getElementById("zoom_ctrl");
  654. var v = zoom_control.value;
  655. prev_zm = zm;
  656. if (zooms[v]) {
  657. if (!zooms[v].is_updated) zooms[v].refresh();
  658. } else {
  659. zooms[v] = new tzoom(v);
  660. }
  661. if (zooms[v].is_updated) {
  662. if (shx == undefined || shy == undefined) {
  663. shx=0;
  664. shy=0;
  665. }
  666. zm = zooms[v];
  667. var px = (last.x+shx)*zm.im.width/prev_zm.im.width - shx;
  668. var py = (last.y+shy)*zm.im.height/prev_zm.im.height - shy;
  669. if (py < zm.im.height && py >= 0) {
  670. zoom = zm.value;
  671. tile = zm.tile;
  672. ntiles = zm.ntiles;
  673. update_url();
  674. putImage(px, py);
  675. } else {
  676. zm = prev_zm;
  677. zoom_control.value = zm.value;
  678. }
  679. }
  680. }
  681. function change_angle() {
  682. var elvtn_control = document.getElementById('elvtn_ctrl');
  683. var angle_control = document.getElementById('angle_ctrl');
  684. var resxy = zm.get_pos_xy(angle_control.value, elvtn_control.value);
  685. var pos_x = resxy.x;
  686. var pos_y = Math.floor(zm.im.height/2 - resxy.y);
  687. putImage(pos_x, pos_y);
  688. }
  689. function check_prox(x, y, r) { //verification si un point de coordonnées x, y est bien dans un cercle de rayon r centré en X,Y.
  690. return Math.sqrt(x*x + y*y) < r;
  691. }
  692. function check_links(e) {
  693. var mouse_x = e.pageX-canvas_pos.x;
  694. var mouse_y = e.pageY-canvas_pos.y;
  695. var pos_x = nmodulo(last.x + mouse_x - canvas.width/2, zm.im.width);
  696. var pos_y = last.y + mouse_y - canvas.height/2;
  697. for(var i = 0; i < zm.pt_list.length; i++) {
  698. if (is_located && zm.pt_list[i]['type'] == 'pano_point') {
  699. if (check_prox(zm.pt_list[i]['xc']-pos_x, zm.pt_list[i]['yc']-pos_y, 20)) {
  700. if (zm.pt_list[i]['lnk'] != '') window.location = zm.pt_list[i]['lnk'];
  701. break;
  702. }
  703. }
  704. }
  705. }
  706. function display_links(e) {
  707. var index = {};
  708. if (e.touches) {
  709. e.preventDefault();
  710. index.x = e.changedTouches[0].clientX;
  711. index.y = e.changedTouches[0].clientY;
  712. } else {
  713. index.x = e.pageX;
  714. index.y = e.pageY;
  715. }
  716. var info = document.getElementById('info');
  717. var mouse_x = index.x-canvas_pos.x;
  718. var mouse_y = index.y-canvas_pos.y;
  719. var pos_x = nmodulo(last.x + mouse_x - canvas.width/2, zm.im.width);
  720. var pos_y = last.y + mouse_y - canvas.height/2;
  721. //var cap = ((pos_x/zm.im.width)*360).toFixed(2);
  722. var res = zm.get_cap_ele(pos_x, zm.im.height/2 - pos_y);
  723. //var ele = ((zm.im.height/2 - pos_y)/zm.im.width)*360;
  724. info.innerHTML = 'élévation : '+res.ele.toFixed(2)+'&#176;<br/>cap : '+res.cap.toFixed(2)+'&#176;';
  725. info.style.top = index.y+'px';
  726. info.style.left = index.x+'px';
  727. info.style.backgroundColor = '#222';
  728. info.style.opacity = '0.6'
  729. info.style.color = 'white'
  730. info.style.display = 'block';
  731. canvas.style.cursor='crosshair';
  732. for(var i = 0; i < zm.pt_list.length; i++) {
  733. if (is_located || zm.pt_list[i]['type'] == 'ref_point') {
  734. if (check_prox(zm.pt_list[i]['xc']-pos_x, zm.pt_list[i]['yc']-pos_y, 20)) {
  735. info.innerHTML = zm.pt_list[i]['label'];
  736. info.style.opacity = '0.8';
  737. info.style.color = 'black';
  738. info.innerHTML += '<br/>(' + zm.pt_list[i]['dist_human'] + ')';
  739. info.style.backgroundColor = 'rgb('+point_colors[zm.pt_list[i]['type']]+')';
  740. canvas.style.cursor='auto';
  741. break;
  742. }
  743. }
  744. }
  745. }
  746. function hide_links() {
  747. canvas.removeEventListener( "mousemove", display_links, false);
  748. canvas.removeEventListener( "touchmove", display_links, false);
  749. var info = document.getElementById('info');
  750. info.style.display = 'none';
  751. }
  752. function show_links() {
  753. canvas.addEventListener( "mousemove", display_links, false);
  754. canvas.addEventListener( "touchmove", display_links, false);
  755. // var info = document.getElementById('info');
  756. // info.style.display = 'block';
  757. }
  758. function hide_contextmenu() {
  759. document.getElementById('insert').style.display = 'none';
  760. }
  761. function manage_ref_points(e) {
  762. //event.preventDefault();
  763. //event.stopPropagation();
  764. var sel_pt = document.getElementById('sel_point');
  765. var do_insert = document.getElementById('do-insert');
  766. var do_delete = document.getElementById('do-delete');
  767. var do_cancel = document.getElementById('do-cancel');
  768. //var show_cap = document.getElementById('show-cap');
  769. var insrt = document.getElementById('insert');
  770. var pos_x = nmodulo(last.x + e.pageX - canvas_pos.x - canvas.width/2, zm.im.width);
  771. var pos_y = last.y + e.pageY - canvas_pos.y - canvas.height/2;
  772. insrt.style.left = e.pageX+'px';
  773. insrt.style.top = e.pageY+'px';
  774. insrt.style.display = 'block';
  775. if (do_insert) {// true if there are ref points
  776. for(var i = 0; i < zm.pt_list.length; i++) {
  777. if (zm.pt_list[i]['type'] == 'ref_point') {
  778. if (check_prox(zm.pt_list[i]['xc']-pos_x, zm.pt_list[i]['yc']-pos_y, 20)) {
  779. sel_pt.value = zm.pt_list[i]['label'];
  780. }
  781. }
  782. }
  783. do_delete.onclick = function() {delete_ref_point(insrt)};
  784. do_insert.onclick = function() {insert_ref_point(insrt, e.pageX-canvas_pos.x, e.pageY-canvas_pos.y)};
  785. // TODO: adapt to the new backend
  786. //show_cap.onclick = function() {
  787. // window.open('show_capline.php?title='+encodeURIComponent(btoa(title))+'&cap='+res.cap+'&org_lat='+pt_lat+'&org_lon='+pt_lon+'&dist=120000');
  788. //};
  789. }
  790. do_cancel.onclick = hide_contextmenu;
  791. var res = zm.get_cap_ele(pos_x, zm.im.height/2 - pos_y);
  792. var pt_lat = document.getElementById('pos_lat').childNodes[0].nodeValue;
  793. var pt_lon = document.getElementById('pos_lon').childNodes[0].nodeValue;
  794. return false;
  795. }
  796. function insert_ref_point(el, x, y) {
  797. var label, posx, posy;
  798. el.style.display = 'none';
  799. var selected_label = document.getElementById('sel_point').value;
  800. var found = false;
  801. var refpoint_url;
  802. for(var i = 0; i < zm.pt_list.length; i++) {
  803. label = zm.pt_list[i]['label'];
  804. if(label == selected_label) {
  805. refpoint_url = zm.pt_list[i]['url'];
  806. posx = nmodulo(last.x + x - canvas.width/2, zm.im.width)/zm.im.width;
  807. posy = 0.5 - (last.y + y - canvas.height/2)/zm.im.height;
  808. var pval = {x:posx, y:posy, cap:zm.pt_list[i]['cap'], ele:zm.pt_list[i]['ele'], label:label};
  809. ref_points[label] = pval;
  810. reset_zooms();
  811. putImage(last.x, last.y);
  812. found = true;
  813. break;
  814. }
  815. }
  816. if (!found) {
  817. alert('unknown ref_point: ' + selected_label);
  818. }
  819. // Then push the modif
  820. var xhr = getXMLHttpRequest();
  821. xhr.open("POST", "/api/v1/references/", true);
  822. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  823. xhr.setRequestHeader("X-CSRFToken", csrf_token);
  824. xhr.send("reference_point=" + refpoint_url
  825. + "&panorama=" + panorama_url
  826. + "&x=" + Math.floor(posx * image_width)
  827. + "&y=" + Math.floor((posy + 0.5) * image_height));
  828. // update the course of the panorama boundaries
  829. // (update cap_min/cap_max of the panorama object)
  830. var xhr = getXMLHttpRequest();
  831. xhr.open("POST", "/api/v1/panoramas/", true);
  832. xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
  833. xhr.setRequestHeader("X-CSRFToken", csrf_token);
  834. }
  835. function delete_ref_point(el) {
  836. var ref_name = document.getElementById('sel_point').value;
  837. el.style.display = 'none';
  838. var url = ref_points[ref_name].url;
  839. delete ref_points[ref_name];
  840. reset_zooms();
  841. putImage(last.x, last.y);
  842. // Then push the modif
  843. var xhr = getXMLHttpRequest();
  844. xhr.open("DELETE", url, true);
  845. xhr.setRequestHeader("X-CSRFToken", csrf_token);
  846. xhr.send();
  847. }
  848. function clean_canvas_events(e) {
  849. canvas.removeEventListener('mousemove', stickImage, false);
  850. canvas.removeEventListener('touchmove', stickImage, false);
  851. document.getElementById('info').style.display = 'none';
  852. speed.x = 0;
  853. speed.y = 0;
  854. }
  855. canvas_set_size = function() {
  856. canvas.style.border = border_width+"px solid black";
  857. canvas.width = window.innerWidth-2*border_width;
  858. canvas.height = window.innerHeight-2*border_width;
  859. canvas_pos.x = canvas.offsetLeft+border_width;
  860. canvas_pos.y = canvas.offsetTop+border_width;
  861. }
  862. canvas_resize = function() {
  863. canvas_set_size();
  864. putImage(last.x, last.y);
  865. }
  866. function paramIn(e) {
  867. e = e || window.event;
  868. var relatedTarget = e.relatedTarget || e.fromElement;
  869. while (relatedTarget != adding && relatedTarget.nodeName != 'BODY' && relatedTarget != document && relatedTarget != localisation) {
  870. relatedTarget = relatedTarget.parentNode;
  871. }
  872. if (relatedTarget != adding && relatedTarget != localisation) {
  873. document.removeEventListener('keydown', keys, false);
  874. }
  875. }
  876. function paramOut(e) {
  877. e = e || window.event;
  878. var relatedTarget = e.relatedTarget || e.toElement;
  879. while (relatedTarget != adding && relatedTarget.nodeName != 'BODY' && relatedTarget != document && relatedTarget != localisation) {
  880. relatedTarget = relatedTarget.parentNode;
  881. }
  882. if (relatedTarget != adding && relatedTarget != localisation) {
  883. document.addEventListener('keydown', keys, false);
  884. }
  885. }
  886. /* Parse initial orientation from URL, either:
  887. #zoom=A/x=B/y=C
  888. #zoom=A/cap=B/elev=C
  889. In the first case, (x, y) is an image coordinate in pixels, where (0, 0)
  890. is the lower left corner.
  891. In the second case, point to the given cap and elevation, assuming the
  892. current panorama is already calibrated.
  893. */
  894. function get_orientation_from_url() {
  895. // Parse window.location.hash to get either x/y or cap/ele
  896. var regexp1 = /^#zoom=(\d)\/cap=(-?\d+|-?\d+\.\d+)\/ele=(-?\d+|-?\d+\.\d+)$/;
  897. var regexp2 = /^#zoom=(\d)\/x=(\d+)\/y=(\d+)$/;
  898. var res = window.location.hash.match(regexp1);
  899. if (res) {
  900. return { zoom: parseInt(res[1], 10),
  901. cap: parseFloat(res[2]),
  902. elevation: parseFloat(res[3]) };
  903. }
  904. else {
  905. res = window.location.hash.match(regexp2);
  906. if (res) {
  907. return { zoom: parseInt(res[1], 10),
  908. x: parseInt(res[2], 10),
  909. y: parseInt(res[3], 10) };
  910. }
  911. else {
  912. /* By default, center the view */
  913. return { zoom: 2, x: image_width / 2, y: image_height / 2 };
  914. }
  915. }
  916. }
  917. /* Update the URL to reflect the current zoom/orientation, so that it acts
  918. * as a permalink. */
  919. function update_url() {
  920. var x = last.x << zm.value;
  921. var y = image_height - (last.y << zm.value);
  922. // Important: don't set window.location.hash directly, because it
  923. // records a new entry in the browser history...
  924. window.location.replace("#zoom=" + zm.value + "/x=" + x + "/y=" + y);
  925. }
  926. function load_pano() {
  927. localisation = document.getElementById("locadraw");
  928. adding = document.getElementById("adding");
  929. canvas = document.getElementById("mon-canvas");
  930. cntext = canvas.getContext("2d");
  931. canvas_set_size();
  932. canvas.addEventListener("click", check_links, false);
  933. //canvas.addEventListener("oncontextmenu", manage_ref_points, false);
  934. canvas.oncontextmenu = manage_ref_points;
  935. canvas.addEventListener("mouseout" , clean_canvas_events, false);
  936. show_links();
  937. var initial_orientation = get_orientation_from_url();
  938. var to_zoom = initial_orientation.zoom;
  939. var max_zoom = zooms.length - 1;
  940. zoom_control = document.getElementById("zoom_ctrl");
  941. zoom_control.onchange = change_zoom;
  942. zoom_control.max = max_zoom;
  943. if (to_zoom > max_zoom) to_zoom = Math.floor(max_zoom/2);
  944. zm = zooms[to_zoom];
  945. zoom_control.value = to_zoom;
  946. zm.refresh();
  947. zoom = zm.value;
  948. tile = zm.tile;
  949. ntiles = zm.ntiles;
  950. if (!("cap" in initial_orientation)) {
  951. /* Compute cap and elevation from (x, y) coordinates */
  952. var res = zm.get_cap_ele(initial_orientation.x >> zoom,
  953. (initial_orientation.y - image_height / 2) >> zoom);
  954. initial_orientation.cap = res.cap;
  955. initial_orientation.elevation = res.ele;
  956. }
  957. angle_control = document.getElementById("angle_ctrl");
  958. angle_control.value = initial_orientation.cap;
  959. angle_control.addEventListener('change', change_angle, false);
  960. angle_control.addEventListener('onclick', change_angle, false);
  961. elvtn_control = document.getElementById("elvtn_ctrl");
  962. elvtn_control.value = initial_orientation.elevation;
  963. elvtn_control.onchange = change_angle;
  964. elvtn_control.onclick = change_angle;
  965. change_angle();
  966. loca_temp = document.getElementById("loca_show");
  967. if (loca_temp) {
  968. loca_temp.onclick = showLoca;
  969. loca_temp = document.getElementById("loca_hide");
  970. loca_temp.onclick = hideLoca;
  971. loca_temp = document.getElementById("loca_button");
  972. loca_temp.onclick = localate_point;
  973. loca_erase = document.getElementById("loca_erase");
  974. loca_erase.onclick = erase_point;
  975. localisation.addEventListener('mouseover',paramIn,false);
  976. localisation.addEventListener('mouseout',paramOut,false);
  977. }
  978. canvas.addEventListener('mousedown', onImageClick, false);
  979. canvas.addEventListener('touchstart', onImageClick, false);
  980. document.addEventListener('keydown', keys, false);
  981. canvas.addEventListener('mousewheel', wheel_zoom, false);
  982. canvas.addEventListener('DOMMouseScroll', wheel_zoom, false);
  983. window.onresize = canvas_resize;
  984. if (adding) {
  985. document.getElementById("paramFormHide").onclick = hideForm;
  986. document.getElementById("paramFormShow").onclick = showForm;
  987. adding.addEventListener('mouseover', paramIn, false);
  988. adding.addEventListener('mouseout', paramOut, false);
  989. }
  990. };
  991. function toRad(n) {
  992. return n * Math.PI / 180;
  993. }
  994. function toDeg(n) {
  995. return n * 180 / Math.PI;
  996. }
  997. function destVincenty(lat1, lon1, brng, dist) {
  998. /* JavaScript function to calculate the destination point given start point
  999. * latitude / longitude (numeric degrees), bearing (numeric degrees) and
  1000. * distance (in m).
  1001. * Original scripts by Chris Veness
  1002. * Taken from http://movable-type.co.uk/scripts/latlong-vincenty-direct.html
  1003. * and optimized / cleaned up by Mathias Bynens <http://mathiasbynens.be/>
  1004. *
  1005. * Based on the Vincenty direct formula by T. Vincenty, “Direct and Inverse
  1006. * Solutions of Geodesics on the Ellipsoid with application of nested
  1007. * equations”, Survey Review, vol XXII no 176, 1975
  1008. * <http://www.ngs.noaa.gov/PUBS_LIB/inverse.pdf>
  1009. */
  1010. var a = 6378137,
  1011. b = 6356752.3142,
  1012. f = 1 / 298.257223563, // WGS-84 ellipsiod
  1013. s = dist,
  1014. alpha1 = toRad(brng),
  1015. sinAlpha1 = Math.sin(alpha1),
  1016. cosAlpha1 = Math.cos(alpha1),
  1017. tanU1 = (1 - f) * Math.tan(toRad(lat1)),
  1018. cosU1 = 1 / Math.sqrt((1 + tanU1 * tanU1)), sinU1 = tanU1 * cosU1,
  1019. sigma1 = Math.atan2(tanU1, cosAlpha1),
  1020. sinAlpha = cosU1 * sinAlpha1,
  1021. cosSqAlpha = 1 - sinAlpha * sinAlpha,
  1022. uSq = cosSqAlpha * (a * a - b * b) / (b * b),
  1023. A = 1 + uSq / 16384 * (4096 + uSq * (-768 + uSq * (320 - 175 * uSq))),
  1024. B = uSq / 1024 * (256 + uSq * (-128 + uSq * (74 - 47 * uSq))),
  1025. sigma = s / (b * A),
  1026. sigmaP = 2 * Math.PI;
  1027. while (Math.abs(sigma - sigmaP) > 1e-12) {
  1028. var cos2SigmaM = Math.cos(2 * sigma1 + sigma),
  1029. sinSigma = Math.sin(sigma),
  1030. cosSigma = Math.cos(sigma),
  1031. deltaSigma = B * sinSigma * (cos2SigmaM + B / 4 * (cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM) - B / 6 * cos2SigmaM * (-3 + 4 * sinSigma * sinSigma) * (-3 + 4 * cos2SigmaM * cos2SigmaM)));
  1032. sigmaP = sigma;
  1033. sigma = s / (b * A) + deltaSigma;
  1034. };
  1035. var tmp = sinU1 * sinSigma - cosU1 * cosSigma * cosAlpha1,
  1036. lat2 = Math.atan2(sinU1 * cosSigma + cosU1 * sinSigma * cosAlpha1, (1 - f) * Math.sqrt(sinAlpha * sinAlpha + tmp * tmp)),
  1037. lambda = Math.atan2(sinSigma * sinAlpha1, cosU1 * cosSigma - sinU1 * sinSigma * cosAlpha1),
  1038. C = f / 16 * cosSqAlpha * (4 + f * (4 - 3 * cosSqAlpha)),
  1039. Lo = lambda - (1 - C) * f * sinAlpha * (sigma + C * sinSigma * (cos2SigmaM + C * cosSigma * (-1 + 2 * cos2SigmaM * cos2SigmaM))),
  1040. revAz = Math.atan2(sinAlpha, -tmp); // final bearing
  1041. return {lat: toDeg(lat2), lng: lon1 + toDeg(Lo)};
  1042. };
  1043. /*
  1044. function getCone(lat, lng, bearing, angle, distance){
  1045. // Returns a polygon to be drawn to the map to show the current visual field
  1046. //
  1047. var conepoints = [];
  1048. // by default, points are drawn every 5°, but if the angle to draw is
  1049. // smaller than 5°, we draw no intermediary points
  1050. var delta = 5;
  1051. if (angle < 5){delta=angle};
  1052. conepoints.push ([lat,lng]);
  1053. for (i=0; i<=angle; i+=delta){
  1054. conepoints.push([destVincenty(lat, lng, bearing-(angle/2.0)+i, distance).lat,
  1055. destVincenty(lat, lng, bearing-(angle/2.0)+i, distance).lng]);
  1056. }
  1057. var p = L.polygon(conepoints, {
  1058. color: 'grey',
  1059. fillColor: 'grey',
  1060. fillOpacity: 0.5
  1061. });
  1062. return p;
  1063. };
  1064. */
  1065. function getCone(lat, lng, bearing, cap, distance){
  1066. /* Returns a polygon to be drawn to the map to show the current visual field
  1067. */
  1068. var conepoints = [];
  1069. // by default, points are drawn every 5°, plus the end-point.
  1070. var delta = 5;
  1071. var total_angle = cap.cap_max - cap.cap_min;
  1072. if (cap.cap_max<cap.cap_min){total_angle+=360}
  1073. conepoints.push ([lat,lng]);
  1074. for (i=0; i<=total_angle; i+=delta){
  1075. angle = cap.cap_min+i;
  1076. if (angle > 360){angle-=360}
  1077. conepoints.push([destVincenty(lat, lng, angle, distance).lat,
  1078. destVincenty(lat, lng, angle, distance).lng])
  1079. }
  1080. // add extrem point
  1081. conepoints.push([destVincenty(lat, lng, cap.cap_max, distance).lat,
  1082. destVincenty(lat, lng, cap.cap_max, distance).lng])
  1083. var p = L.polygon(conepoints, {
  1084. color: 'black',
  1085. weight: '1',
  1086. fillColor: 'grey',
  1087. fillOpacity: 0.5
  1088. });
  1089. return p;
  1090. };
  1091. function getCapMinMaxVisible(){
  1092. /* Return the minimun and maximum cap visible
  1093. */
  1094. var cw = canvas.width;
  1095. var initial_orientation = get_orientation_from_url();
  1096. var x = initial_orientation.x ;
  1097. var to_zoom = initial_orientation.zoom ;
  1098. zm = zooms[to_zoom];
  1099. // x min and max visible in the screen
  1100. // 1 pixel_screen = X pixel_photo = pixel_photo / ((nb_tiles-1) * pixel_tile + pixel_last_tile)
  1101. // (nb_tiles-1)*pixel_tile + pixel_last_tile = zm.im.visible_width
  1102. var half_width = (cw - 2*border_width) * ( image_width / (zm.im.visible_width)) / 2 ;
  1103. var total_angle = fmodulo(image_cap_max - image_cap_min, 360); // panorama total angle
  1104. if (total_angle == 0){total_angle = 360}; // if panorama loops
  1105. var half_angle = total_angle * (half_width / image_width);
  1106. // min and max visible cap
  1107. var bearing = parseInt($('#angle_ctrl').val());
  1108. var cap_min = fmodulo(bearing - half_angle, 360);
  1109. var cap_max = fmodulo(bearing + half_angle, 360);
  1110. // check outside border
  1111. if (! image_loop){
  1112. if (x - half_width < 0){cap_min = image_cap_min};
  1113. if (x + half_width > image_width){cap_max = image_cap_max};
  1114. };
  1115. // check if we repeat the pano
  1116. if ( half_angle > 180 && image_loop){
  1117. var cap_min = 0;
  1118. var cap_max = 360;
  1119. };
  1120. return {cap_min: cap_min, cap_max : cap_max}
  1121. };
  1122. function load_map(){
  1123. /* Create the map object with the view cone and bearing object
  1124. */
  1125. // initialize the map object (global, to be view from update_map())
  1126. map = L.map('mapid').setView([panorama_lat, panorama_lng], 13);
  1127. // create the tile layer with correct attribution
  1128. var osmUrl='https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png';
  1129. var osmAttrib='Map data © <a href="https://openstreetmap.org">OpenStreetMap</a> contributors';
  1130. var osm = new L.TileLayer(osmUrl, {attribution: osmAttrib, maxZoom: 19});
  1131. map.addLayer(osm);
  1132. map.addLayer( markerClusters );
  1133. map.addLayer(pointsOfInterest);
  1134. L.marker([panorama_lat, panorama_lng]).addTo(map);
  1135. map.on('click', function(e) {
  1136. /* Compute the new cap to show given the clic lat/lng */
  1137. var lat = toRad(e.latlng.lat); // latitude
  1138. var lng = toRad(e.latlng.lng); // longitude
  1139. var lat_ref = toRad(panorama_lat);
  1140. var lng_ref = toRad(panorama_lng);
  1141. // angle between the ref_point and the clic_point
  1142. var y = Math.sin(lng-lng_ref) * Math.cos(lat);
  1143. var x = Math.cos(lat_ref)*Math.sin(lat) -
  1144. Math.sin(lat_ref)*Math.cos(lat)*Math.cos(lng-lng_ref);
  1145. var brng = Math.atan2(y, x);
  1146. var brng = toDeg(brng)
  1147. var newCap = brng
  1148. // The cap is between -180 and 180, and we want 0 360, so convert
  1149. // negative value (-180 -> -0 to 180 360).
  1150. if (newCap < 0 ){
  1151. newCap = 360 + brng
  1152. }
  1153. // Ensure the cap is between cap_min and cap_max
  1154. // first, let cap_min be 0,
  1155. // then, rot the other angles (if it's not a 360 degree)
  1156. // Here is a hack of the modulus operator for negative value
  1157. // Last, if the new cap is greater than the max, then set it to the max
  1158. var rot = image_cap_min
  1159. var a_min = 0
  1160. if (image_cap_max != 360) {
  1161. var a_max = ((image_cap_max - rot) % 360 + 360) % 360
  1162. }
  1163. var b = ((newCap - rot) % 360 + 360) % 360
  1164. if (b > a_max){
  1165. var meanAngle = a_max+(360-a_max)/2
  1166. if (b < meanAngle){
  1167. newCap = image_cap_max;
  1168. }else{
  1169. newCap = image_cap_min;
  1170. }
  1171. }
  1172. // change the cap
  1173. angle_control = document.getElementById('angle_ctrl');
  1174. angle_control.value = newCap;
  1175. // update the panorama & minimap
  1176. change_angle(); // update panorama & minimap
  1177. });
  1178. hide_map()
  1179. };
  1180. function update_map(){
  1181. /* Update the map: view cone and bearing
  1182. */
  1183. if (map_never_drawn){
  1184. map_never_drawn = false;
  1185. } else {
  1186. map.removeLayer(viewField);
  1187. map.removeLayer(viewDirection);
  1188. };
  1189. var bearing = $('#angle_ctrl').val();
  1190. var cap = getCapMinMaxVisible();
  1191. /* TODO: allow to configure the maximum distance. */
  1192. viewField = getCone(panorama_lat,panorama_lng,bearing,cap,50000);
  1193. viewDirection = L.polygon([[panorama_lat, panorama_lng],[destVincenty(panorama_lat, panorama_lng, bearing, 50000).lat,destVincenty(panorama_lat, panorama_lng, bearing, 50000).lng]],{color: '#2880ca', opacity: 1, weight: 2});
  1194. viewField.addTo(map);
  1195. viewDirection.addTo(map);
  1196. }
  1197. function hide_map(){
  1198. /* Hide or show the map on panorama view
  1199. */
  1200. var map = $('#mapid')
  1201. $('#expandmap').click(function() {
  1202. if(map.css('visibility')=='visible'){
  1203. map.css('visibility','hidden');
  1204. $('#expandmap').css({'bottom': '12px','right':'12px'})
  1205. $('#expandmap').attr('class', 'fa fa-expand');
  1206. } else {
  1207. map.css('visibility','visible');
  1208. $('#expandmap').css({'bottom':'266px','right':'12px'})
  1209. $('#expandmap').attr('class', 'fa fa-compress');
  1210. }
  1211. });
  1212. }