minor things added

upa
Hansi, dl9rdz 2021-01-25 19:27:50 +01:00
rodzic a3d3a69ef0
commit 9bc26db65b
8 zmienionych plików z 699 dodań i 26 usunięć

10
LICENSE-EasyButton 100644
Wyświetl plik

@ -0,0 +1,10 @@
css/easy-button.css
js/easy-button.js
Copyright (C) 2014 Daniel Montague
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

Wyświetl plik

@ -15,13 +15,15 @@
"devDependencies": {
"cordova-android": "^9.0.0",
"cordova-browser": "^6.0.0",
"cordova-plugin-inappbrowser": "^4.1.0",
"cordova-plugin-whitelist": "^1.3.4",
"de-dl9rdz-rdzwx": "https://github.com/dl9rdz/rdzwx-plugin.git"
},
"cordova": {
"plugins": {
"cordova-plugin-whitelist": {},
"de-dl9rdz-rdzwx": {}
"de-dl9rdz-rdzwx": {},
"cordova-plugin-inappbrowser": {}
},
"platforms": [
"android"

Wyświetl plik

@ -0,0 +1,56 @@
.leaflet-bar button,
.leaflet-bar button:hover {
background-color: #fff;
border: none;
border-bottom: 1px solid #ccc;
width: 26px;
height: 26px;
line-height: 26px;
display: block;
text-align: center;
text-decoration: none;
color: black;
}
.leaflet-bar button {
background-position: 50% 50%;
background-repeat: no-repeat;
overflow: hidden;
display: block;
}
.leaflet-bar button:hover {
background-color: #f4f4f4;
}
.leaflet-bar button:first-of-type {
border-top-left-radius: 4px;
border-top-right-radius: 4px;
}
.leaflet-bar button:last-of-type {
border-bottom-left-radius: 4px;
border-bottom-right-radius: 4px;
border-bottom: none;
}
.leaflet-bar.disabled,
.leaflet-bar button.disabled {
cursor: default;
pointer-events: none;
opacity: .4;
}
.easy-button-button .button-state{
display: block;
width: 100%;
height: 100%;
position: relative;
}
.leaflet-touch .leaflet-bar button {
width: 30px;
height: 30px;
line-height: 30px;
}

Wyświetl plik

@ -33,18 +33,63 @@ body {
padding:0px;
/* Padding to avoid the "unsafe" areas behind notches in the screen */
padding: env(safe-area-inset-top, 0px) env(safe-area-inset-right, 0px) env(safe-area-inset-bottom, 0px) env(safe-area-inset-left, 0px);
text-transform:uppercase;
width:100%;
}
html, body, #map {
html, body {
height: 100%;
width: 100cv;
}
#info {
height: 20%;
#all {
height: 100%;
width: 100cv;
}
#map {
height: 100%;
width: 100cv;
}
.sondeTooltip {
color: #333;
font-size: 11px;
font-weight: bold;
}
.text-speed {
color: #008cba;
}
.fitbutton {
font-size: 1.5em;
}
.target {
font-size: 1.5em;
}
.infobutton {
font-size: 1.5em;
font-family: 'Courier New', monospace;
}
.leaflet-center {
position: relative !important;
left: 0;
right: 0;
align-items: center;
display: flex;
flex-direction: column;
justify-content: center;
}
.leaflet-center .leaflet-control {
bottom: 0;
}
.leaflet-control-container .leaflet-control-bottomcenter {
position: absolute;
bottom: 0;
left: 0;
right: 0;
}
/* Portrait layout (default) */
.app {

BIN
www/img/landing.png 100644

Plik binarny nie jest wyświetlany.

Po

Szerokość:  |  Wysokość:  |  Rozmiar: 578 B

Wyświetl plik

@ -36,13 +36,22 @@
<meta name="color-scheme" content="light dark">
<link rel="stylesheet" href="css/index.css">
<link rel="stylesheet" href="css/leaflet.css">
<script src="cordova.js"></script>
<script src="js/leaflet.js"></script>
<link rel="stylesheet" href="css/easy-button.css">
<!--
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.css">
<script src="https://cdn.jsdelivr.net/npm/leaflet-easybutton@2/src/easy-button.js"></script>
-->
<script src="js/easy-button.js"></script>
<title>Hello World</title>
</head>
<body>
<div id='map'></div>
<div id='info'></div>
<script src="cordova.js"></script>
<div id="all">
<div id='map'></div>
<!-- <div id='info'></div> -->
</div>
<script src="js/index.js"></script>
</body>
</html>

Wyświetl plik

@ -0,0 +1,376 @@
(function(){
// This is for grouping buttons into a bar
// takes an array of `L.easyButton`s and
// then the usual `.addTo(map)`
L.Control.EasyBar = L.Control.extend({
options: {
position: 'topleft', // part of leaflet's defaults
id: null, // an id to tag the Bar with
leafletClasses: true // use leaflet classes?
},
initialize: function(buttons, options){
if(options){
L.Util.setOptions( this, options );
}
this._buildContainer();
this._buttons = [];
for(var i = 0; i < buttons.length; i++){
buttons[i]._bar = this;
buttons[i]._container = buttons[i].button;
this._buttons.push(buttons[i]);
this.container.appendChild(buttons[i].button);
}
},
_buildContainer: function(){
this._container = this.container = L.DomUtil.create('div', '');
this.options.leafletClasses && L.DomUtil.addClass(this.container, 'leaflet-bar easy-button-container leaflet-control');
this.options.id && (this.container.id = this.options.id);
},
enable: function(){
L.DomUtil.addClass(this.container, 'enabled');
L.DomUtil.removeClass(this.container, 'disabled');
this.container.setAttribute('aria-hidden', 'false');
return this;
},
disable: function(){
L.DomUtil.addClass(this.container, 'disabled');
L.DomUtil.removeClass(this.container, 'enabled');
this.container.setAttribute('aria-hidden', 'true');
return this;
},
onAdd: function () {
return this.container;
},
addTo: function (map) {
this._map = map;
for(var i = 0; i < this._buttons.length; i++){
this._buttons[i]._map = map;
}
var container = this._container = this.onAdd(map),
pos = this.getPosition(),
corner = map._controlCorners[pos];
L.DomUtil.addClass(container, 'leaflet-control');
if (pos.indexOf('bottom') !== -1) {
corner.insertBefore(container, corner.firstChild);
} else {
corner.appendChild(container);
}
return this;
}
});
L.easyBar = function(){
var args = [L.Control.EasyBar];
for(var i = 0; i < arguments.length; i++){
args.push( arguments[i] );
}
return new (Function.prototype.bind.apply(L.Control.EasyBar, args));
};
// L.EasyButton is the actual buttons
// can be called without being grouped into a bar
L.Control.EasyButton = L.Control.extend({
options: {
position: 'topleft', // part of leaflet's defaults
id: null, // an id to tag the button with
type: 'replace', // [(replace|animate)]
// replace swaps out elements
// animate changes classes with all elements inserted
states: [], // state names look like this
// {
// stateName: 'untracked',
// onClick: function(){ handle_nav_manually(); };
// title: 'click to make inactive',
// icon: 'fa-circle', // wrapped with <a>
// }
leafletClasses: true, // use leaflet styles for the button
tagName: 'button',
},
initialize: function(icon, onClick, title, id){
// clear the states manually
this.options.states = [];
// add id to options
if(id != null){
this.options.id = id;
}
// storage between state functions
this.storage = {};
// is the last item an object?
if( typeof arguments[arguments.length-1] === 'object' ){
// if so, it should be the options
L.Util.setOptions( this, arguments[arguments.length-1] );
}
// if there aren't any states in options
// use the early params
if( this.options.states.length === 0 &&
typeof icon === 'string' &&
typeof onClick === 'function'){
// turn the options object into a state
this.options.states.push({
icon: icon,
onClick: onClick,
title: typeof title === 'string' ? title : ''
});
}
// curate and move user's states into
// the _states for internal use
this._states = [];
for(var i = 0; i < this.options.states.length; i++){
this._states.push( new State(this.options.states[i], this) );
}
this._buildButton();
this._activateState(this._states[0]);
},
_buildButton: function(){
this.button = L.DomUtil.create(this.options.tagName, '');
if (this.options.tagName === 'button') {
this.button.setAttribute('type', 'button');
}
if (this.options.id ){
this.button.id = this.options.id;
}
if (this.options.leafletClasses){
L.DomUtil.addClass(this.button, 'easy-button-button leaflet-bar-part leaflet-interactive');
}
// don't let double clicks and mousedown get to the map
L.DomEvent.addListener(this.button, 'dblclick', L.DomEvent.stop);
L.DomEvent.addListener(this.button, 'mousedown', L.DomEvent.stop);
L.DomEvent.addListener(this.button, 'mouseup', L.DomEvent.stop);
// take care of normal clicks
L.DomEvent.addListener(this.button,'click', function(e){
L.DomEvent.stop(e);
this._currentState.onClick(this, this._map ? this._map : null );
this._map && this._map.getContainer().focus();
}, this);
// prep the contents of the control
if(this.options.type == 'replace'){
this.button.appendChild(this._currentState.icon);
} else {
for(var i=0;i<this._states.length;i++){
this.button.appendChild(this._states[i].icon);
}
}
},
_currentState: {
// placeholder content
stateName: 'unnamed',
icon: (function(){ return document.createElement('span'); })()
},
_states: null, // populated on init
state: function(newState){
// when called with no args, it's a getter
if (arguments.length === 0) {
return this._currentState.stateName;
}
// activate by name
if(typeof newState == 'string'){
this._activateStateNamed(newState);
// activate by index
} else if (typeof newState == 'number'){
this._activateState(this._states[newState]);
}
return this;
},
_activateStateNamed: function(stateName){
for(var i = 0; i < this._states.length; i++){
if( this._states[i].stateName == stateName ){
this._activateState( this._states[i] );
}
}
},
_activateState: function(newState){
if( newState === this._currentState ){
// don't touch the dom if it'll just be the same after
return;
} else {
// swap out elements... if you're into that kind of thing
if( this.options.type == 'replace' ){
this.button.appendChild(newState.icon);
this.button.removeChild(this._currentState.icon);
}
if( newState.title ){
this.button.title = newState.title;
} else {
this.button.removeAttribute('title');
}
// update classes for animations
for(var i=0;i<this._states.length;i++){
L.DomUtil.removeClass(this._states[i].icon, this._currentState.stateName + '-active');
L.DomUtil.addClass(this._states[i].icon, newState.stateName + '-active');
}
// update classes for animations
L.DomUtil.removeClass(this.button, this._currentState.stateName + '-active');
L.DomUtil.addClass(this.button, newState.stateName + '-active');
// update the record
this._currentState = newState;
}
},
enable: function(){
L.DomUtil.addClass(this.button, 'enabled');
L.DomUtil.removeClass(this.button, 'disabled');
this.button.setAttribute('aria-hidden', 'false');
return this;
},
disable: function(){
L.DomUtil.addClass(this.button, 'disabled');
L.DomUtil.removeClass(this.button, 'enabled');
this.button.setAttribute('aria-hidden', 'true');
return this;
},
onAdd: function(map){
var bar = L.easyBar([this], {
position: this.options.position,
leafletClasses: this.options.leafletClasses
});
this._anonymousBar = bar;
this._container = bar.container;
return this._anonymousBar.container;
},
removeFrom: function (map) {
if (this._map === map)
this.remove();
return this;
},
});
L.easyButton = function(/* args will pass automatically */){
var args = Array.prototype.concat.apply([L.Control.EasyButton],arguments);
return new (Function.prototype.bind.apply(L.Control.EasyButton, args));
};
/*************************
*
* util functions
*
*************************/
// constructor for states so only curated
// states end up getting called
function State(template, easyButton){
this.title = template.title;
this.stateName = template.stateName ? template.stateName : 'unnamed-state';
// build the wrapper
this.icon = L.DomUtil.create('span', '');
L.DomUtil.addClass(this.icon, 'button-state state-' + this.stateName.replace(/(^\s*|\s*$)/g,''));
this.icon.innerHTML = buildIcon(template.icon);
this.onClick = L.Util.bind(template.onClick?template.onClick:function(){}, easyButton);
}
function buildIcon(ambiguousIconString) {
var tmpIcon;
// does this look like html? (i.e. not a class)
if( ambiguousIconString.match(/[&;=<>"']/) ){
// if so, the user should have put in html
// so move forward as such
tmpIcon = ambiguousIconString;
// then it wasn't html, so
// it's a class list, figure out what kind
} else {
ambiguousIconString = ambiguousIconString.replace(/(^\s*|\s*$)/g,'');
tmpIcon = L.DomUtil.create('span', '');
if( ambiguousIconString.indexOf('fa-') === 0 ){
L.DomUtil.addClass(tmpIcon, 'fa ' + ambiguousIconString)
} else if ( ambiguousIconString.indexOf('glyphicon-') === 0 ) {
L.DomUtil.addClass(tmpIcon, 'glyphicon ' + ambiguousIconString)
} else {
L.DomUtil.addClass(tmpIcon, /*rollwithit*/ ambiguousIconString)
}
// make this a string so that it's easy to set innerHTML below
tmpIcon = tmpIcon.outerHTML;
}
return tmpIcon;
}
})();

Wyświetl plik

@ -23,9 +23,30 @@ document.addEventListener('deviceready', onDeviceReady, false);
// map from sondeid to marker (and path)
var markers = {};
var mypos;
var ready = 0;
var map = null;
var lastObj = { obj: null, pred: null, land: null };
//var mypos = {lat: 48.56, lon: 13.43};
var mypos = {lat: 48.1, lon: 13.1};
var ballonIcon, landIcon;
var infobox = null;
var checkMark = "&#9989;";
var crossMark = "&#x274C;";
// add "bottom center" to leaflet
(function (L) {
L.Map.prototype._initControlPos = function(_initControlPos) {
return function() {
_initControlPos.apply(this, arguments); // original function
this._controlCorners['bottomcenter'] = L.DomUtil.create('div', 'leaflet-bottom leaflet-center',
L.DomUtil.create('div', 'leaflet-control-bottomcenter', this._controlContainer)
);
};
} (L.Map.prototype._initControlPos);
}(L, this, document));
function onDeviceReady() {
// Cordova is now initialized. Have fun!
@ -68,19 +89,164 @@ function onDeviceReady() {
var baseMapControl = new L.control.layers(baseMaps, {}, { collapsed: true, position: 'topright' } ).addTo(map);
baseMaps["Openstreetmap"].addTo(map);
//map.addEventListener('baselayerchange', function(){ map.fire('click'); });
// not working.......... map.addEventListener('baselayerchange', baseMapControl.collapse() );
L.control.scale({metric: true, imperial: false, position: "bottomright"}).addTo(map);
//L.tileLayer("https://tile.openstreetmap.org/{z}/{x}/{y}.png", {
// attribution: '&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors',
//}).addTo(map);
// prediction
L.easyButton('<span class="target">&target;</span>', function(btn, map) {
getPrediction();
}).addTo(map);
map.locate({setView: true, maxZoom: 16});
RdzWx.start("testarg", callBack);
var Infobox = L.Control.extend({
options: { position: 'bottomcenter' },
onAdd: function(map) {
var infoContainer = L.DomUtil.create('div', 'leaflet-control-layers leaflet-control');
var infoBody = L.DomUtil.create('div', 'leaflet-popup-content-wrapper');
infoContainer.appendChild(infoBody);
infoBody.setAttribute('style', 'max-width: 100vw');
var infoContent = L.DomUtil.create('div', 'leaflet-popup-content');
infoBody.appendChild(infoContent);
var infoCloseButton = L.DomUtil.create('a', 'leaflet-popup-close-button');
infoContainer.appendChild(infoCloseButton);
infoCloseButton.innerHTML = 'x';
infoCloseButton.setAttribute('style', 'cursor: pointer');
this._infoContainer = infoContainer;
this._infoBody = infoBody;
this._infoContentContainer = infoContent;
this._infoCloseButton = infoCloseButton;
infoContent.innerHTML = 'This is the inner content';
this._hideContent();
L.DomEvent.disableClickPropagation(infoContainer);
L.DomEvent.on(infoCloseButton, 'click', L.DomEvent.stop);
L.DomEvent.on(infoCloseButton, 'click', this._hideContent, this);
return infoContainer;
},
toggle: function() {
if(this._contentShown == false) { this._showContent(); } else { this._hideContent(); }
},
setContent: function(content) {
this._infoContent = content;
if(this._infoContentContainer) { this._infoContentContainer.innerHTML = content; }
},
_hideContent: function(ev) {
this._infoBody.style.display = 'none';
this._infoCloseButton.style.display = 'none';
this._contentShown = false;
},
_showContent: function(ev) {
this._infoBody.style.display = '';
this._infoCloseButton.style.display = '';
this._contentShown = true;
},
});
infobox = new Infobox();
infobox.addTo(map);
// button to show/hide info box on bottom
L.easyButton('<span class="infobutton">I</span>', function(btn, map) {
infobox.toggle();
}).addTo(map);
// fit map to enclosing rectangle of (last pos, own pos)
L.easyButton('<span class="fitbutton">&#9635;</span>', function(btn, map) {
// last item
if(lastObj.obj == null) return;
// self position
if(mypos == null) return;
var items = [ [lastObj.obj.lat, lastObj.obj.lon], [mypos.lat, mypos.lon] ];
if(lastObj.land) { items.push( lastObj.land.getLatLng() ); }
b = L.latLngBounds(items);
map.fitBounds(b);
}).addTo(map);
ttgoStatus = L.easyButton( {
ttgourl: "http://192.168.42.1",
states: [{ stateName: 'offline',
icon: '<span class="ttgostatus">' + crossMark + '</span>',
onClick: function(btn, map) { btn.state('online'); }
},
{ stateName: 'online',
icon: '<span class="ttgostatus">' + checkMark + '</span>',
onClick: function(btn, map) { cordova.InAppBrowser.open(btn.ttgourl, '_blank', "location=yes"); }
}
],
position: "topright"
});
ttgoStatus.state('offline');
ttgoStatus.addTo(map);
// '<span class="ttgosttus">&#9989;</span>', )
ballonIcon = L.icon({
iconUrl: "img/ballon.png",
iconSize: [17,22],
iconAnchor: [9,22],
popupAnchor: [0,-28]
});
landingIcon = L.icon({
iconUrl: "img/landing.png",
iconSize: [24,24],
iconAnchor: [12,12],
popupAnchor: [0,0]
});
ready = 1;
RdzWx.start("testarg", callBack);
// just for testing
update( {id: "A1234567", lat: 48, lon: 13, alt: 10000, vs: 10, hs: 30} );
}
function formatParams(params) {
return '?' + Object.keys(params).map( function(key) {
return key+"="+encodeURIComponent(params[key])
}).join('&');
}
function getPrediction() {
TAWHIRI = 'http://predict.cusf.co.uk/api/v1';
if(lastObj.obj == null) {
alert("no object available");
return;
}
var tParams = {
"launch_latitude": lastObj.obj.lat,
"launch_longitude": lastObj.obj.lon,
"launch_altitude": lastObj.obj.alt,
"launch_datetime": new Date().toISOString().split('.')[0] + 'Z',
"ascent_rate": 5,
"descent_rate": 3,
"burst_altitude": lastObj.obj.alt+2,
"profile": "standard_profile",
}
const xhr = new XMLHttpRequest();
const url = TAWHIRI + formatParams(tParams);
xhr.onreadystatechange = function() {
if(xhr.readyState === 4) {
console.log(xhr.response);
var pred = JSON.parse(xhr.response);
var traj = pred.prediction[1]; // 0 is ascent, 1 is descent...
traj = traj.trajectory;
var latlons = [];
traj.forEach( p => latlons.push( [p.latitude, p.longitude] ) );
//alert("path: "+JSON.stringify(traj));
poly = L.polyline(latlons, { opacity: 0.5, color: '#EE0000', dashArray: '8, 6'} );
poly.addTo(map);
if( lastObj.pred ) { lastObj.pred.remove(map); }
lastObj.pred = poly;
if( lastObj.land ) { lastObj.land.remove(map); }
lastObj.land = new L.marker(latlons.slice(-1)[0], {icon: landingIcon});
lastObj.land.addTo(map);
}
}
xhr.open('GET', url, true);
xhr.send(null);
}
function callBack(arg) {
@ -92,7 +258,7 @@ function callBack(arg) {
return;
}
console.log("callback: "+JSON.stringify(arg));
if(obj.id) {
if(obj.id || obj.msgtype) {
update(obj);
}
}
@ -103,32 +269,41 @@ function update(obj) {
console.log("not ready");
return;
}
if(obj.msgtype) {
if(obj.msgtype == "ttgostatus") {
ttgoStatus.ttgourl = 'http://' + obj.ip;
ttgoStatus.state(obj.state)
}
// TODO: add GPS messages
return;
}
lastObj.obj = obj;
var pos = new L.LatLng(obj.lat, obj.lon);
var marker;
var tooltip;
if(markers[obj.id]) {
marker = markers[obj.id];
if(pos.equals(marker.getLatLng())) { console.log("update: position unchanged"); }
else { marker.path.addLatLng(pos); console.log("update: appending new position"); }
tooltip = marker.tt;
} else {
console.log("creating new marker");
var myIcon = L.icon({
//iconUrl: "https://unpkg.com/leaflet@1.7.1/dist/images/marker-icon.png",
iconUrl: "img/ballon.png",
iconSize: [17,22],
iconAnchor: [9,22],
popupAnchor: [0,-28],
});
marker = new L.marker(pos, {icon: myIcon});
marker = new L.marker(pos, {icon: ballonIcon});
poly = L.polyline(pos, { opacity: 0.5, color: '#3388ff'} );
marker.path = poly;
//marker.on('clock', function() { showMarkerInfoWindow( obj.id, pos) } );
markers[obj.id] = marker;
marker.addTo(map);
poly.addTo(map);
tooltip = L.tooltip({ direction: 'right', permanent: true, className: 'sondeTooltip', offset: [10,0], interactive: false, opacity: 0.6 });
marker.bindTooltip(tooltip);
marker.tt = tooltip;
}
//var icon = new Icon();
//marker.set
var tt = '<div class="tooltip-container">' + obj.id + '<div class="text-speed tooltip-container">' + obj.alt + 'm '+ obj.vs +'m/s ' + (obj.hs*3.6).toFixed(1) + 'km/h </div></div>';
tooltip.setContent(tt);
marker.setLatLng(pos);
marker.update(); // necessary?
}