Stalker PDA





Description
Once, I wondered if it was possible to create a mobile application based on web technologies. Because sites are made on the same technology stack, and they work on all devices. So there must be a way to convert site into a mobile application so that it works on all devices. I really wanted a universal solution. And there is not to need to learn different programming languages to create mobile applications for a specific operating system, for example, for Android or iOS. Looking for information, I stumbled upon Cordova and Phonegap, which work through a WebView.
Since I am interested in urban explorations (stalking), especially stalk of Chernobyl Exclusion Zone (more in the video from YouTube than in reality, but so... :D), I decided to create a Stalker PDA. This is a mobile assistant for a real stalker of Chernobyl Exclusion Zone, which stores offline maps of the CEZ, with all objects on it, real-time weather map, stalker anecdotes, stalker name generator, dark theme, some entertainment, and even an online chat for stalkers.
About two weeks of development and the application was completely ready, except for the online chat. As a result, I realized that for chatting it need to rent a server, and for placement in online markets such as App Store or Play Market, it need to buy a developer account that is not cheap, so I abandoned this project. But it remained in my history, and is still on my phone - it is very convenient sometimes to look at the weather online :)
Download: StalkerPDA-v1.1.1.apk
Details
| Technologies | Cordova, Phonegap, NodeJS, Gulp, Javascript, jQuery, Ajax, SASS, PostCSS, SVG, API, Leaflet, Geolocation |
|---|---|
| Features | Aviable on all devices, Map of Chernobyl Exclusion Zone with objects on it, Real-time weather map, Stalker anecdotes from whole trilogy S.T.A.L.K.E.R. game, Stalker name generator, theme switching with storage, Offline map saver |
| Date | August 2020 |
| Link | https://zakandaiev.github.io/stalker-pda |
| Github | https://github.com/zakandaiev/stalker-pda |
Code snippets
js Cordova map geolocation
// MAP GEOLOCATION
var audio_pda_geo = document.createElement('audio');
audio_pda_geo.setAttribute('src', 'audio/pda_news.ogg');
var current_position, current_accuracy;
function geolocateMe() {
$("#geolocation-btn .btn-icon").addClass("active");
var onLocationFound = function(position) {
var lat_lng = [position.coords.latitude,position.coords.longitude];
var accuracy = position.coords.accuracy;
// if position defined, then remove the existing position marker and accuracy circle from the map
if (current_position) {
home_map.removeLayer(current_position);
home_map.removeLayer(current_accuracy);
}
var radius = Math.round(accuracy / 2);
current_position = L.marker(lat_lng, {icon: current_loc_marker_icon}).addTo(home_map)
.bindPopup("Вы находитесь в радиусе " + radius + " м от центра круга");
home_map.flyTo(lat_lng, 18);
current_accuracy = L.circle(lat_lng, radius).addTo(home_map);
current_position.openPopup();
$("#geolocation-btn .btn-icon").removeClass("active");
if (current_theme === 'stalker') {
audio_pda_geo.play();
}
};
function onLocationError(error) {
navigator.notification.alert(
error.message, // message
null, // callback
'Ошибка', // title
'Ок' // buttonName
);
$("#geolocation-btn .btn-icon").removeClass("active");
}
navigator.geolocation.getCurrentPosition(onLocationFound, onLocationError, {maximumAge: 30000,enableHighAccuracy:true});
}
$("#geolocation-btn").on("click", function() {
navigator.geolocation.activator.askActivation(function(response) {
geolocateMe();
}, function(response) {
navigator.notification.alert(
'Для этой функции необходимо разрешить и включить GPS!', // message
null, // callback
'Внимание', // title
'Ок' // buttonName
);
});
});
js Leaflet map ruler
// MAP RULER
$("#ruler-btn").on("click", function() {
if (home_map.measureControl.handler.enabled()) {
home_map.measureControl.handler.disable.call(home_map.measureControl.handler);
$("#ruler-btn .btn-icon").removeClass("active");
} else {
home_map.measureControl.handler.enable.call(home_map.measureControl.handler);
$("#ruler-btn .btn-icon").addClass("active");
}
});
js Cordova open external links in system web browser
// cordova open external links in system web browser
$("a[target='_blank']").click(function(e){
e.preventDefault();
window.open($(e.currentTarget).attr('href'), '_system', '');
});
html Weather map legend
<div class="weather-legend">
<div class="weather-legend__item">
<div class="weather-legend__square cloud"></div>
<span>Моросящий дождь (вероятно)</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square percip-1"></div>
<span>Моросящий дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square percip-2"></div>
<span>Слабый дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square percip-3"></div>
<span>Слабый дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square rainfall-1"></div>
<span>Дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square rainfall-2"></div>
<span>Дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square rainfall-3"></div>
<span>Дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square storm-1"></div>
<span>Дождь</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square storm-2"></div>
<span>Гроза</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square storm-3"></div>
<span>Гроза</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square hail-1"></div>
<span>Град</span>
</div>
<div class="weather-legend__item">
<div class="weather-legend__square hail-2"></div>
<span>Град</span>
</div>
</div>
scss Weather map legend
.weather-legend {
position: absolute;
z-index: 2;
top: 50%;
transform: translateY(-50%);
left: 4px;
max-height: calc(100vh - 92px);
overflow: hidden auto;
padding: 4px;
background: #fff;
box-shadow: $shadow-2;
border-radius: 4px;
display: flex;
flex-direction: column;
width: 24px;
cursor: pointer;
transition: all .3s;
&__item {
display: flex;
align-items: center;
font-size: 12px;
line-height: 1;
}
&.is-open {
width: 198px;
}
span {
margin-left: 4px;
white-space: nowrap;
}
&__square {
flex-shrink: 0;
width: 16px;
height: 16px;
&.cloud {
background: #8ee;
}
&.percip-1 {
background: #0099cc;
}
&.percip-2 {
background: #0077aa;
}
&.percip-3 {
background: #005588;
}
&.rainfall-1 {
background: #ffee00;
}
&.rainfall-2 {
background: #ffaa00;
}
&.rainfall-3 {
background: #ff7700;
}
&.storm-1 {
background: #ff4400;
}
&.storm-2 {
background: #ee0000;
}
&.storm-3 {
background: #990000;
}
&.hail-1 {
background: #ffaaff;
}
&.hail-2 {
background: #ff77ff;
}
}
}