Compare commits

...

70 commits

Author SHA1 Message Date
simon
419edcac29 clean up 2020-09-14 15:19:57 +03:00
Victor Shcherb
b5a24dc23b Fix viewport change 2020-09-11 11:25:22 +02:00
Victor Shcherb
9a35b70773 Fix viewport change 2020-09-11 11:18:19 +02:00
Victor Shcherb
31a3ec3b05 Fix viewport change 2020-09-11 11:08:23 +02:00
Victor Shcherb
dcc05f7129 Fix http server 2020-09-11 11:01:58 +02:00
simon
e070f5be94 zoom started 2020-09-10 18:00:50 +03:00
simon
b708f8e6ff time checks removed 2020-09-10 17:12:46 +03:00
simon
65e8328a0c Merge branch 'android_http_server' of https://github.com/osmandapp/Osmand into android_http_server 2020-09-10 16:54:42 +03:00
simon
1d3849ef4d time checks 2020-09-10 16:54:38 +03:00
Victor Shcherb
98f0823bbb Update constant 2020-09-10 15:38:34 +02:00
Victor Shcherb
ff236c7d81 Update constants 2020-09-10 15:35:20 +02:00
Victor Shcherb
d62a92b5e8 Set screen view detached 2020-09-10 11:57:11 +02:00
Victor Shcherb
c914e18fc1 Fix float format 2020-09-10 11:37:36 +02:00
Victor Shcherb
1b85b5a732 Clean up code 2020-09-10 11:26:18 +02:00
Victor Shcherb
ad82cd419f Small fixes~ 2020-09-10 11:10:08 +02:00
Victor Shcherb
9d37cff564 Small fixes~ 2020-09-10 11:06:10 +02:00
simon
d0c6270a00 cache added 2020-09-10 11:45:32 +03:00
simon
6c86997d2c crash fixed 2020-09-09 18:04:56 +03:00
simon
2cd4e23463 cache added 2020-09-09 18:00:33 +03:00
simon
576e70a606 update 2020-09-09 15:44:39 +03:00
simon
ce2b0eacbf metatile cache added 2020-09-09 13:28:14 +03:00
simon
2851fd38cd map syncronization fixed 2020-09-08 16:23:43 +03:00
simon
463679afd3 js tile baseurl update 2020-09-08 15:53:40 +03:00
simon
ad444aadaf tiles added 2020-09-08 15:32:39 +03:00
simon
2442759d07 Merge branch 'android_http_server' of https://github.com/osmandapp/Osmand into android_http_server 2020-09-07 19:58:11 +03:00
Victor Shcherb
b779b8b61f Introduce cache 2020-09-07 18:57:54 +02:00
simon
f34b1fdf11 Merge branch 'android_http_server' of https://github.com/osmandapp/Osmand into android_http_server 2020-09-07 19:38:50 +03:00
Victor Shcherb
a92bac55f7 Fix exception 2020-09-07 18:37:12 +02:00
simon
96653b3749 conflict resolved 2020-09-07 19:27:47 +03:00
simon
ee54d374ff my local changes 2020-09-07 19:27:02 +03:00
Victor Shcherb
2b9db97fb7 Fix tile endpoints 2020-09-07 18:26:40 +02:00
Victor Shcherb
b3cb492558 Fix some errors 2020-09-07 18:14:52 +02:00
simon
08ab7a6613 cleanup 2020-09-07 17:32:21 +03:00
simon
150e769709 refactoring 2020-09-07 17:30:27 +03:00
simon
485ee88a13 remove useless html 2020-09-07 15:35:03 +03:00
simon
aeff02d633 mapactivity added 2020-09-07 12:37:56 +03:00
simon
b4d4e79b6a small changes 2020-09-07 12:08:57 +03:00
simon
cdde9f3960 whitespace change 2020-09-07 12:02:02 +03:00
simon
23b871ee22 whitespace change 2020-09-07 12:01:22 +03:00
simon
7ae7f45c48 whitespace change 2020-09-07 11:59:09 +03:00
simon
cb9c141c64 whitespace change 2020-09-07 11:58:05 +03:00
simon
9ed5eb95c7 whitespace change 2020-09-07 11:54:01 +03:00
simon
aa1932e3d2 whitespace change 2020-09-07 11:52:46 +03:00
simon
fbeba45ada whitespace change 2020-09-07 11:50:51 +03:00
simon
9fc7d9ef26 whitespace change 2020-09-07 11:48:06 +03:00
simon
96ad964beb log removed 2020-09-07 11:44:40 +03:00
simon
920241a043 cleanup 2020-09-07 11:37:39 +03:00
simon
8505f67d68 style issues 2020-09-07 11:30:51 +03:00
simon
8dd5bda3fc Merge branch 'android_http_server' of https://github.com/osmandapp/Osmand into android_http_server 2020-09-07 09:34:12 +03:00
simon
9112d4a34f endpoint routing fix 2020-09-07 09:33:35 +03:00
vshcherb
edb3aede9e
Merge branch 'master' into android_http_server 2020-09-04 23:53:38 +02:00
simon
a7633b2031 refactoring3 2020-09-04 18:48:01 +03:00
simon
fef6f93621 refactoring 2020-09-04 18:22:24 +03:00
simon
9823461435 refactoring 2020-09-04 17:38:36 +03:00
simon
d1451e9889 style fixes 2020-09-04 16:49:24 +03:00
simon
c436b1dcb8 import rearrage 2020-09-04 16:46:44 +03:00
simon
b846969e49 cleanup and refactoring 2020-09-04 16:43:54 +03:00
simon
2f1707167c tabs issue resolved 2020-09-04 15:05:03 +03:00
simon
656a316f87 code cleanup 2020-09-04 14:55:47 +03:00
simon
c81e5a88ae html page to stream 2020-09-03 20:34:38 +03:00
simon
2869cae3a0 still cleanup 2020-09-03 20:15:31 +03:00
simon
1f8dbb7694 code cleanup 2020-09-03 16:53:38 +03:00
simon
71c895c81e build failed fix 2020-09-03 14:41:53 +03:00
simon
40bc660320 Merge branch 'master' of git://github.com/osmandapp/Osmand into android_http_server 2020-09-03 14:40:32 +03:00
simon
c0c07b95c2 Merge branch 'master' of git://github.com/osmandapp/Osmand into android_http_server 2020-09-03 14:33:50 +03:00
simon
065755d5b2 manifest fix 2020-09-03 14:33:32 +03:00
simon
8f4b8c8349 server activity added 2020-09-03 14:21:55 +03:00
simon
3ad4e5fc71 adding new files 2020-09-03 14:19:04 +03:00
simon
409aa0574a Merge branch 'master' of git://github.com/osmandapp/Osmand into android_http_server 2020-09-03 13:52:11 +03:00
Victor Shcherb
c2395016f9 Squashed commit of the following:
commit 9a8a4e16b2
Author: simon <myhosting2012@gmail.com>
Date:   Thu Sep 3 00:13:28 2020 +0300

    server refactoring1

commit e47493a229
Author: simon <myhosting2012@gmail.com>
Date:   Wed Sep 2 23:31:12 2020 +0300

    server refactoring

commit 6a95630076
Author: simon <myhosting2012@gmail.com>
Date:   Wed Sep 2 18:40:42 2020 +0300

    api router mapactivity changes

commit e6a345e0e9
Author: simon <myhosting2012@gmail.com>
Date:   Mon Aug 31 18:10:11 2020 +0300

    add variables lat lon zoom

commit 04bb04f21c
Author: simon <myhosting2012@gmail.com>
Date:   Mon Aug 31 16:45:54 2020 +0300

    tiles by lat lon

commit 3019e35d04
Author: simon <myhosting2012@gmail.com>
Date:   Mon Aug 31 15:45:53 2020 +0300

    changing resourceManager

commit 8a8fcd8d58
Author: simon <myhosting2012@gmail.com>
Date:   Mon Aug 31 15:26:28 2020 +0300

    cleanup

commit 3f9b0f03d4
Author: simon <myhosting2012@gmail.com>
Date:   Mon Aug 31 11:35:00 2020 +0300

    display loaded tile

commit db49b925ca
Author: simon <myhosting2012@gmail.com>
Date:   Thu Aug 27 14:29:57 2020 +0300

    saving tile response

commit ef2bc3677c
Author: simon <myhosting2012@gmail.com>
Date:   Tue Aug 25 13:17:33 2020 +0300

    markers coloured
2020-09-03 12:39:18 +02:00
22 changed files with 4689 additions and 24 deletions

View file

@ -8,6 +8,7 @@ public interface OsmAndCustomizationConstants {
String DRAWER_MAP_MARKERS_ID = DRAWER_ITEM_ID_SCHEME + "map_markers"; String DRAWER_MAP_MARKERS_ID = DRAWER_ITEM_ID_SCHEME + "map_markers";
String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places"; String DRAWER_MY_PLACES_ID = DRAWER_ITEM_ID_SCHEME + "my_places";
String DRAWER_SEARCH_ID = DRAWER_ITEM_ID_SCHEME + "search"; String DRAWER_SEARCH_ID = DRAWER_ITEM_ID_SCHEME + "search";
String DRAWER_WEBSERVER_ID = DRAWER_ITEM_ID_SCHEME + "server";
String DRAWER_DIRECTIONS_ID = DRAWER_ITEM_ID_SCHEME + "directions"; String DRAWER_DIRECTIONS_ID = DRAWER_ITEM_ID_SCHEME + "directions";
String DRAWER_CONFIGURE_MAP_ID = DRAWER_ITEM_ID_SCHEME + "configure_map"; String DRAWER_CONFIGURE_MAP_ID = DRAWER_ITEM_ID_SCHEME + "configure_map";
String DRAWER_DOWNLOAD_MAPS_ID = DRAWER_ITEM_ID_SCHEME + "download_maps"; String DRAWER_DOWNLOAD_MAPS_ID = DRAWER_ITEM_ID_SCHEME + "download_maps";

File diff suppressed because it is too large Load diff

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View file

@ -0,0 +1,81 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="cache-control" content="max-age=0"/>
<meta http-equiv="cache-control" content="no-cache"/>
<meta http-equiv="expires" content="0"/>
<meta http-equiv="expires" content="Tue, 01 Jan 1980 1:00:00 GMT"/>
<meta http-equiv="pragma" content="no-cache"/>
<meta http-equiv="X-UA-Compatible" content="IE=Edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>OsmAnd - Offline Mobile Maps and Navigation</title>
<link rel="stylesheet" type="text/css" href="/css/site.css?v=4"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.1.0/dist/leaflet.css"/>
<script src="https://unpkg.com/leaflet@1.1.0/dist/leaflet.js"></script>
<script type="text/javascript" src="/scripts/jquery-3.1.0.min.js"></script>
<script src="/scripts/leaflet.awesome-markers.js"></script>
<link rel="stylesheet" href="http://code.ionicframework.com/ionicons/1.5.2/css/ionicons.min.css">
<script type="text/javascript" src="/scripts/go.js?v=5"></script>
<script>
function getMarkerContent(feature) {
var p = feature.properties;
var popupContent = p.title;
if (p.opr_id) {
popupContent += " (" + p.opr_id + ") ";
}
if (p.tags) {
for (var t in p.tags) {
popupContent += "<br>" + t + " " + p.tags[t];
}
}
if (p.opr_id) {
popupContent += "<br><a href=\'/api/admin?view=objects&browse=type&type=opr.place&subtype=id&key=" +
p.opr_id + "\'>OpenPlaceReviews</a>";
}
if (p.osm_id) {
popupContent += "<br><a href=\'https://www.openstreetmap.org/" +
p.osm_type + "/" + p.osm_id + "\'>OpenStreetMap</a>";
}
return popupContent;
}
function markerClick(e) {
}
function setupMarkers() {
$.ajax({
type: 'GET',
url: "/favorites",
async: false,
contentType: "application/json",
dataType: 'json',
complete: function (r) {
var points = JSON.parse(r.responseText);
points.forEach(e => {
window.goMap.map.addPopupMarker(e, markerClick);
});
},
error: function (e) {
alert("Error happened while getting favourite points " + e.message);
}
});
}
// $(document).ready(function () {
// //setupMarkers();
// });
</script>
</head>
<body>
<div class="gocontainer" id="gocontainer">
<div class="goheader">
<a href="/"><img src="/images/logo-grey.png" class="logo"/></a>
</div>
<div id="map"></div>
</div>
</body>
</html>

View file

@ -0,0 +1,273 @@
var requestUtils={
'getParamValue':function(paramName){
let value= (location.search.split(paramName + '=')[1]||'').split('&')[0];
if (value && value.length > 0){
return value;
}
return null;
},
'isIOS':function(){
return /(iPad|iPhone|iPod)/g.test( navigator.userAgent );
},
'redirect':function(newUrl){
document.location = newUrl;
}
};
var goMap = {
'config':{
'containerid': 'gocontainer',
'defaults':{
'lat':50.27,
'lon':30.30,
'zoom':13
}
},
'utils':{
'getPointFromUrl':function(){
let point = {};
point.lat = requestUtils.getParamValue('lat');
point.lon = requestUtils.getParamValue('lon');
point.zoom = requestUtils.getParamValue('z');
return point;
},
'isPointComplete':function(point){
if (!point.lat || !point.lon){
return false;
}
return true;
},
'extendPoint':function(initialPoint, newPoint){
let point={};
point.lat=newPoint.lat;
if (!point.lat || point.lat == null){
point.lat = initialPoint.lat;
}
point.lon=newPoint.lon;
if (!point.lon || point.lon == null){
point.lon = initialPoint.lon;
}
point.zoom=newPoint.zoom;
if (!point.zoom || point.zoom == null){
point.zoom = initialPoint.zoom;
}
return point;
}
},
'init': function(config){
if (config && typeof (config) == 'object') {
$.extend(goMap.config, config);
}
goMap.$container = $('#' + goMap.config.containerid);
goMap.$footer = goMap.$container.find('.gofooter');
goMap.$latitude = goMap.$container.find('.latitude');
goMap.$longitude = goMap.$container.find('.longitude');
let inputPoint = goMap.utils.getPointFromUrl();
goMap.point = goMap.utils.extendPoint(goMap.config.defaults, inputPoint);
goMap.refreshCoordinates();
goMap.map =$.mapwidget();
goMap.map.showPoint(goMap.point);
let inputComplete = goMap.utils.isPointComplete(inputPoint);
if (inputComplete){
goMap.map.addMarker(goMap.point);
}
goMap.point = goMap.utils.getPointFromUrl();
},
'refreshCoordinates':function(){
goMap.$latitude.text(goMap.point.lat);
goMap.$longitude.text(goMap.point.lon);
}
};
function toColor(num) {
num >>>= 0;
var b = num & 0xFF,
g = (num & 0xFF00) >>> 8,
r = (num & 0xFF0000) >>> 16,
a = ( (num & 0xFF000000) >>> 24 ) / 255 ;
return "rgba(" + [r, g, b, a].join(",") + ")";
}
(function($) {
$.mapwidget = function(config) {
var loc = goMap.point.lat + '/' + goMap.point.lon;
var lparams = '?mlat='+goMap.point.lat + '&mlon=' + goMap.point.lon;
var mapobj={
config: $.extend({
'mapid':'map',
'maxzoom':20,
'maxnativezoom':19,
'sourceurl': window.location.protocol + "//" + window.location.host + '/tile/{z}/{x}/{y}.png',
'attribution':'&copy; <a href="https://www.openstreetmap.org/'+lparams+'#map=15/'+loc+'">OpenStreetMap</a> contributors'
}, config),
init:function(){
mapobj.map = L.map(mapobj.config.mapid);
L.tileLayer(mapobj.config.sourceurl, {
attribution: mapobj.config.attribution,
maxZoom: mapobj.config.maxzoom,
maxNativeZoom: mapobj.config.maxnativezoom
}).addTo(mapobj.map);
},
showPoint:function(point){
mapobj.map.setView([point.lat, point.lon], point.zoom);
},
addMarker:function(point){
L.marker([point.lat, point.lon]).addTo(mapobj.map);
},
addPopupMarker:function(favorite,onClickEvent){
window.point = favorite;
var point = {};
point.lat = favorite.latitude;
point.lon = favorite.longitude;
var popup = L.popup().setContent(
"name: <b>" + favorite.name + "</b><br/>" +
"address: <i>" + favorite.address + "</i><br/>"
+ "category: " + favorite.category);
var customMarker = L.AwesomeMarkers.icon({
icon: 'home',
markerColor: toColor(favorite.color),
iconColor: toColor(favorite.color)
});
L.marker([point.lat, point.lon],{icon: customMarker})
.bindPopup(popup)
.addTo(mapobj.map)
.on('click', function(e) {
onClickEvent(e);
});
}
};
mapobj.init();
return {
showPoint: mapobj.showPoint,
addMarker: mapobj.addMarker,
addPopupMarker: mapobj.addPopupMarker
};
};
})(jQuery);
(function($) {
$.timer=function(config){
var timerobj={
config: $.extend({
'timeoutInMs':300,
'maxActionDelayInMs':2000,
'action':function(){},
'actionparams':null
}, config),
init:function(){
timerobj.timer = null;
timerobj.startDate = null;
},
start:function(){
timerobj.cancel();
timerobj.startDate = new Date();
timerobj.timer=setTimeout(timerobj.onTimer, timerobj.config.timeoutInMs);
},
cancel:function(){
if (timerobj.timer != null){
clearTimeout(timerobj.timer);
timerobj.timer = null;
timerobj.startDate = null;
}
},
onTimer:function(){
timerobj.timer= null;
let now = new Date();
if(now - timerobj.startDate < timerobj.config.maxActionDelayInMs){
timerobj.config.action(timerobj.config.actionparams);
}
}
};
timerobj.init();
return {
start:timerobj.start,
cancel:timerobj.cancel
};
};
})(jQuery);
var iosAppRedirect = {
config:{
appPrefix:'osmandmaps://',
containerid:'gocontainer',
cookieName:'OsmAndInstalled',
cookieNoExpirationTimeoutInDays:30
},
init:function(config){
if (config && typeof (config) == 'object') {
$.extend(iosAppRedirect.config, config);
}
if (!requestUtils.isIOS()){
return;
}
iosAppRedirect.$container = $('#' + iosAppRedirect.config.containerid);
iosAppRedirect.$overlay = iosAppRedirect.$container.find('.overlay');
iosAppRedirect.$popup = iosAppRedirect.$container.find('.popup');
iosAppRedirect.$yesBtn = iosAppRedirect.$container.find('.yes');
iosAppRedirect.$noBtn = iosAppRedirect.$container.find('.no');
iosAppRedirect.$cancelBtn = iosAppRedirect.$container.find('.cancel');
iosAppRedirect.applestorelink = iosAppRedirect.$container.find('.gobadges .apple a').attr('href');
iosAppRedirect.applink = iosAppRedirect.config.appPrefix + document.location.search;
if (iosAppRedirect.isAppInstalled() === "yes"){
iosAppRedirect.redirectToApp();
return;
}
if (iosAppRedirect.isAppInstalled() === "no"){
return;
}
iosAppRedirect.$yesBtn.on('click', function(){
iosAppRedirect.redirectToApp();
iosAppRedirect.closePopup();
});
iosAppRedirect.$noBtn.on('click', function(){
iosAppRedirect.setCookie(true);
iosAppRedirect.closePopup();
window.open(iosAppRedirect.applestorelink , '_blank');
});
iosAppRedirect.$cancelBtn.on('click', function(){
iosAppRedirect.setCookie(false);
iosAppRedirect.closePopup();
});
iosAppRedirect.openPopup();
},
isAppInstalled:function(){
return Cookies.get('OsmAndInstalled');
},
redirectToApp:function(){
iosAppRedirect.timer = $.timer({action:iosAppRedirect.clearCookie});
iosAppRedirect.timer.start();
requestUtils.redirect(iosAppRedirect.applink);
},
setCookie:function(appInstalled){
if (appInstalled === true){
Cookies.set(iosAppRedirect.config.cookieName, "yes");
}else{
Cookies.set(iosAppRedirect.config.cookieName, "no", { expires: iosAppRedirect.config.cookieNoExpirationTimeoutInDays });
}
},
clearCookie:function(){
Cookies.remove('OsmAndInstalled');
},
openPopup:function(){
iosAppRedirect.$overlay.show();
iosAppRedirect.$popup.show();
},
closePopup:function(){
iosAppRedirect.$overlay.hide();
iosAppRedirect.$popup.hide();
}
};
$( document ).ready(function() {
goMap.init();
iosAppRedirect.init();
});

File diff suppressed because one or more lines are too long

View file

@ -0,0 +1,165 @@
/*!
* JavaScript Cookie v2.1.4
* https://github.com/js-cookie/js-cookie
*
* Copyright 2006, 2015 Klaus Hartl & Fagner Brack
* Released under the MIT license
*/
;(function (factory) {
var registeredInModuleLoader = false;
if (typeof define === 'function' && define.amd) {
define(factory);
registeredInModuleLoader = true;
}
if (typeof exports === 'object') {
module.exports = factory();
registeredInModuleLoader = true;
}
if (!registeredInModuleLoader) {
var OldCookies = window.Cookies;
var api = window.Cookies = factory();
api.noConflict = function () {
window.Cookies = OldCookies;
return api;
};
}
}(function () {
function extend () {
var i = 0;
var result = {};
for (; i < arguments.length; i++) {
var attributes = arguments[ i ];
for (var key in attributes) {
result[key] = attributes[key];
}
}
return result;
}
function init (converter) {
function api (key, value, attributes) {
var result;
if (typeof document === 'undefined') {
return;
}
// Write
if (arguments.length > 1) {
attributes = extend({
path: '/'
}, api.defaults, attributes);
if (typeof attributes.expires === 'number') {
var expires = new Date();
expires.setMilliseconds(expires.getMilliseconds() + attributes.expires * 864e+5);
attributes.expires = expires;
}
// We're using "expires" because "max-age" is not supported by IE
attributes.expires = attributes.expires ? attributes.expires.toUTCString() : '';
try {
result = JSON.stringify(value);
if (/^[\{\[]/.test(result)) {
value = result;
}
} catch (e) {}
if (!converter.write) {
value = encodeURIComponent(String(value))
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
} else {
value = converter.write(value, key);
}
key = encodeURIComponent(String(key));
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
key = key.replace(/[\(\)]/g, escape);
var stringifiedAttributes = '';
for (var attributeName in attributes) {
if (!attributes[attributeName]) {
continue;
}
stringifiedAttributes += '; ' + attributeName;
if (attributes[attributeName] === true) {
continue;
}
stringifiedAttributes += '=' + attributes[attributeName];
}
return (document.cookie = key + '=' + value + stringifiedAttributes);
}
// Read
if (!key) {
result = {};
}
// To prevent the for loop in the first place assign an empty array
// in case there are no cookies at all. Also prevents odd result when
// calling "get()"
var cookies = document.cookie ? document.cookie.split('; ') : [];
var rdecode = /(%[0-9A-Z]{2})+/g;
var i = 0;
for (; i < cookies.length; i++) {
var parts = cookies[i].split('=');
var cookie = parts.slice(1).join('=');
if (cookie.charAt(0) === '"') {
cookie = cookie.slice(1, -1);
}
try {
var name = parts[0].replace(rdecode, decodeURIComponent);
cookie = converter.read ?
converter.read(cookie, name) : converter(cookie, name) ||
cookie.replace(rdecode, decodeURIComponent);
if (this.json) {
try {
cookie = JSON.parse(cookie);
} catch (e) {}
}
if (key === name) {
result = cookie;
break;
}
if (!key) {
result[name] = cookie;
}
} catch (e) {}
}
return result;
}
api.set = api;
api.get = function (key) {
return api.call(api, key);
};
api.getJSON = function () {
return api.apply({
json: true
}, [].slice.call(arguments));
};
api.defaults = {};
api.remove = function (key, attributes) {
api(key, '', extend(attributes, {
expires: -1
}));
};
api.withConverter = init;
return api;
}
return init(function () {});
}));

View file

@ -0,0 +1,134 @@
/*
Leaflet.AwesomeMarkers, a plugin that adds colorful iconic markers for Leaflet, based on the Font Awesome icons
(c) 2012-2013, Lennard Voogdt
http://leafletjs.com
https://github.com/lvoogdt
*/
/*global L*/
(function (window, document, undefined) {
"use strict";
/*
* Leaflet.AwesomeMarkers assumes that you have already included the Leaflet library.
*/
L.AwesomeMarkers = {};
L.AwesomeMarkers.version = '2.0.1';
L.AwesomeMarkers.Icon = L.Icon.extend({
options: {
shadowAnchor: [10, 12],
shadowSize: [36, 16],
className: 'awesome-marker',
icon: 'block',
markerColor: 'white',
iconColor: 'white'
},
initialize: function (options) {
options = L.Util.setOptions(this, options);
},
createIcon: function () {
var options = L.Util.setOptions(this);
var svg = document.createElementNS("http://www.w3.org/2000/svg", "svg");
var path = document.createElementNS('http://www.w3.org/2000/svg', "path");
var backgroundCircle = document.createElementNS('http://www.w3.org/2000/svg', "circle");
var icongroup = document.createElementNS('http://www.w3.org/2000/svg', "g");
var icon = document.createElementNS('http://www.w3.org/2000/svg', "text");
svg.setAttribute('width', '31');
svg.setAttribute('height', '42');
svg.setAttribute('class', 'awesome-marker');
svg.setAttributeNS("http://www.w3.org/2000/xmlns/", "xmlns:xlink", "http://www.w3.org/1999/xlink");
backgroundCircle.setAttribute('cx', '15.5');
backgroundCircle.setAttribute('cy', '15');
backgroundCircle.setAttribute('r', '11');
backgroundCircle.setAttribute('fill', options.markerColor);
path.setAttributeNS(null, "d", "M15.6,1c-7.7,0-14,6.3-14,14c0,10.5,14,26,14,26s14-15.5,14-26C29.6,7.3,23.3,1,15.6,1z");
//path.setAttribute('class', 'awesome-marker-background');
path.setAttribute('stroke', 'white');
path.setAttribute('style', 'fill:' + options.markerColor)
icon.textContent = options.icon;
icon.setAttribute('x', '7');
icon.setAttribute('y', '23');
icon.setAttribute('class', 'material-icons');
icon.setAttribute('fill', options.iconColor);
icon.setAttribute('font-family', 'Material Icons');
svg.appendChild(path);
svg.appendChild(backgroundCircle);
icongroup.appendChild(icon);
svg.appendChild(icongroup);
return svg;
},
_createInner: function() {
var iconClass, iconSpinClass = "", iconColorClass = "", iconColorStyle = "", options = this.options;
if (options.spin && typeof options.spinClass === "string") {
iconSpinClass = options.spinClass;
}
if (options.iconColor) {
if (options.iconColor === 'white' || options.iconColor === 'black') {
iconColorClass = "icon-" + options.iconColor;
} else {
iconColorStyle = "style='color: " + options.iconColor + "' ";
}
}
//return "<i " + iconColorStyle + "class='" + options.extraClasses + " " + options.prefix + " " + iconClass + " " + iconSpinClass + " " + iconColorClass + "'></i>"
return options.extraClasses + " " + iconClass + " " + iconSpinClass + " " + iconColorClass;
},
_setIconStyles: function (img, name) {
var options = this.options,
size = L.point(options[name === 'shadow' ? 'shadowSize' : 'iconSize']),
anchor;
if (name === 'shadow') {
anchor = L.point(options.shadowAnchor || options.iconAnchor);
} else {
anchor = L.point(options.iconAnchor);
}
if (!anchor && size) {
anchor = size.divideBy(2, true);
}
img.className = 'awesome-marker-' + name + ' ' + options.className;
if (anchor) {
img.style.marginLeft = (-anchor.x) + 'px';
img.style.marginTop = (-anchor.y) + 'px';
}
if (size) {
img.style.width = size.x + 'px';
img.style.height = size.y + 'px';
}
},
createShadow: function () {
var div = document.createElement('div');
this._setIconStyles(div, 'shadow');
return div;
}
});
L.AwesomeMarkers.icon = function (options) {
return new L.AwesomeMarkers.Icon(options);
};
}(this, document));

View file

@ -0,0 +1,7 @@
/*
Leaflet.AwesomeMarkers, a plugin that adds colorful iconic markers for Leaflet, based on the Font Awesome icons
(c) 2012-2013, Lennard Voogdt
http://leafletjs.com
https://github.com/lvoogdt
*//*global L*/(function(e,t,n){"use strict";L.AwesomeMarkers={};L.AwesomeMarkers.version="2.0.1";L.AwesomeMarkers.Icon=L.Icon.extend({options:{iconSize:[35,45],iconAnchor:[17,42],popupAnchor:[1,-32],shadowAnchor:[10,12],shadowSize:[36,16],className:"awesome-marker",prefix:"glyphicon",spinClass:"fa-spin",icon:"home",markerColor:"blue",iconColor:"white"},initialize:function(e){e=L.Util.setOptions(this,e)},createIcon:function(){var e=t.createElement("div"),n=this.options;n.icon&&(e.innerHTML=this._createInner());n.bgPos&&(e.style.backgroundPosition=-n.bgPos.x+"px "+ -n.bgPos.y+"px");this._setIconStyles(e,"icon-"+n.markerColor);return e},_createInner:function(){var e,t="",n="",r="",i=this.options;i.icon.slice(0,i.prefix.length+1)===i.prefix+"-"?e=i.icon:e=i.prefix+"-"+i.icon;i.spin&&typeof i.spinClass=="string"&&(t=i.spinClass);i.iconColor&&(i.iconColor==="white"||i.iconColor==="black"?n="icon-"+i.iconColor:r="style='color: "+i.iconColor+"' ");return"<i "+r+"class='"+i.prefix+" "+e+" "+t+" "+n+"'></i>"},_setIconStyles:function(e,t){var n=this.options,r=L.point(n[t==="shadow"?"shadowSize":"iconSize"]),i;t==="shadow"?i=L.point(n.shadowAnchor||n.iconAnchor):i=L.point(n.iconAnchor);!i&&r&&(i=r.divideBy(2,!0));e.className="awesome-marker-"+t+" "+n.className;if(i){e.style.marginLeft=-i.x+"px";e.style.marginTop=-i.y+"px"}if(r){e.style.width=r.x+"px";e.style.height=r.y+"px"}},createShadow:function(){var e=t.createElement("div");this._setIconStyles(e,"shadow");return e}});L.AwesomeMarkers.icon=function(e){return new L.AwesomeMarkers.Icon(e)}})(this,document);

View file

@ -544,6 +544,7 @@ dependencies {
implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2' implementation 'me.zhanghai.android.materialprogressbar:library:1.4.2'
// JS core // JS core
implementation group: 'org.mozilla', name: 'rhino', version: '1.7.9' implementation group: 'org.mozilla', name: 'rhino', version: '1.7.9'
implementation 'org.nanohttpd:nanohttpd:2.2.0'
// size restrictions // size restrictions
// implementation 'com.ibm.icu:icu4j:50.1' // implementation 'com.ibm.icu:icu4j:50.1'
// implementation 'net.sf.trove4j:trove4j:3.0.3' // implementation 'net.sf.trove4j:trove4j:3.0.3'

View file

@ -0,0 +1,34 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="match_parent"
android:orientation="vertical"
android:padding="32dp"
android:gravity="center"
android:clickable="true"
android:background="@color/color_black"
android:focusable="true">
<TextView
android:id="@+id/server_status_textview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:textAlignment="center"
android:autoLink="web"
android:textColor="@color/color_white"
android:text="@string/start_web_server"
android:gravity="center_horizontal"/>
<Button
android:id="@+id/server_start_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shared_string_start"/>
<Button
android:id="@+id/server_stop_button"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/shared_string_stop"/>
</LinearLayout>

View file

@ -11,6 +11,11 @@
Thx - Hardy Thx - Hardy
--> -->
<string name="shared_string_stop">Stop</string>
<string name="shared_string_start">Start</string>
<string name="start_web_server">Start web server</string>
<string name="stop_web_server">Stop web server</string>
<string name="web_server">Web server</string>
<string name="shared_string_redo">Redo</string> <string name="shared_string_redo">Redo</string>
<string name="one_point_error">Please add at least two points.</string> <string name="one_point_error">Please add at least two points.</string>
<string name="shared_string_is_saved">is saved</string> <string name="shared_string_is_saved">is saved</string>

View file

@ -232,7 +232,9 @@ public class MapViewTrackingUtilities implements OsmAndLocationListener, IMapLoc
switchToRoutePlanningMode(); switchToRoutePlanningMode();
} }
// When location is changed we need to refresh map in order to show movement! // When location is changed we need to refresh map in order to show movement!
mapView.refreshMap(); if (!mapView.isScreenViewDetached()){
mapView.refreshMap();
}
} }
if (dashboard != null) { if (dashboard != null) {

View file

@ -3,8 +3,11 @@ package net.osmand.plus.development;
import android.app.Activity; import android.app.Activity;
import android.content.Intent; import android.content.Intent;
import android.graphics.drawable.Drawable; import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import net.osmand.plus.ContextMenuAdapter; import net.osmand.plus.ContextMenuAdapter;
import net.osmand.plus.ContextMenuItem; import net.osmand.plus.ContextMenuItem;
import net.osmand.plus.OsmandApplication; import net.osmand.plus.OsmandApplication;
@ -14,6 +17,8 @@ import net.osmand.plus.Version;
import net.osmand.plus.activities.ContributionVersionActivity; import net.osmand.plus.activities.ContributionVersionActivity;
import net.osmand.plus.activities.MapActivity; import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.dashboard.tools.DashFragmentData; import net.osmand.plus.dashboard.tools.DashFragmentData;
import net.osmand.plus.firstusage.FirstUsageWizardFragment;
import net.osmand.plus.server.ServerFragment;
import net.osmand.plus.settings.fragments.BaseSettingsFragment; import net.osmand.plus.settings.fragments.BaseSettingsFragment;
import net.osmand.plus.views.layers.MapInfoLayer; import net.osmand.plus.views.layers.MapInfoLayer;
import net.osmand.plus.views.OsmandMapLayer.DrawSettings; import net.osmand.plus.views.OsmandMapLayer.DrawSettings;
@ -21,6 +26,7 @@ import net.osmand.plus.views.OsmandMapTileView;
import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget; import net.osmand.plus.views.mapwidgets.widgets.TextInfoWidget;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_BUILDS_ID; import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_BUILDS_ID;
import static net.osmand.aidlapi.OsmAndCustomizationConstants.DRAWER_WEBSERVER_ID;
public class OsmandDevelopmentPlugin extends OsmandPlugin { public class OsmandDevelopmentPlugin extends OsmandPlugin {
@ -71,10 +77,29 @@ public class OsmandDevelopmentPlugin extends OsmandPlugin {
return true; return true;
} }
}).createItem()); }).createItem());
}
helper.addItem(new ContextMenuItem.ItemBuilder().setTitleId(R.string.web_server, mapActivity)
.setId(DRAWER_WEBSERVER_ID)
.setIcon(R.drawable.mm_shop_computer)
.setListener(new ContextMenuAdapter.ItemClickListener() {
@Override
public boolean onContextMenuClick(ArrayAdapter<ContextMenuItem> adapter, int itemId, int pos, boolean isChecked, int[] viewCoordinates) {
Fragment fragment = new ServerFragment();
showFragment(mapActivity, fragment);
return true;
}
}).createItem());
}
} }
private static void showFragment(FragmentActivity activity, Fragment fragment) {
if (activity != null) {
activity.getSupportFragmentManager()
.beginTransaction()
.replace(R.id.fragmentContainer, fragment, ServerFragment.TAG)
.commitAllowingStateLoss();
}
}
@Override @Override
public void updateLayers(OsmandMapTileView mapView, MapActivity activity) { public void updateLayers(OsmandMapTileView mapView, MapActivity activity) {
if (isActive()) { if (isActive()) {

View file

@ -1,5 +1,6 @@
package net.osmand.plus.render; package net.osmand.plus.render;
import android.util.Log;
import net.osmand.core.android.MapRendererView; import net.osmand.core.android.MapRendererView;
import net.osmand.core.android.TileSourceProxyProvider; import net.osmand.core.android.TileSourceProxyProvider;
import net.osmand.core.jni.MapLayerConfiguration; import net.osmand.core.jni.MapLayerConfiguration;
@ -156,12 +157,19 @@ public class MapVectorLayer extends BaseMapLayer {
mapRenderer.setVisualZoomShift(zoomMagnifier - 1.0f); mapRenderer.setVisualZoomShift(zoomMagnifier - 1.0f);
} else { } else {
if (!view.isZooming()) { if (!view.isZooming()) {
if (resourceManager.updateRenderedMapNeeded(tilesRect, drawSettings)) { if (resourceManager.updateRenderedMapNeeded(tilesRect, drawSettings) ||
view.isScreenViewDetached()) {
// pixRect.set(-view.getWidth(), -view.getHeight() / 2, 2 * view.getWidth(), 3 * // pixRect.set(-view.getWidth(), -view.getHeight() / 2, 2 * view.getWidth(), 3 *
// view.getHeight() / 2); // view.getHeight() / 2);
final RotatedTileBox copy = tilesRect.copy(); final RotatedTileBox copy = tilesRect.copy();
copy.increasePixelDimensions(copy.getPixWidth() / 3, copy.getPixHeight() / 4); Log.d("SERVER: ","SERVER: interrupt in layer");
resourceManager.updateRendererMap(copy, null); if (view.isScreenViewDetached()){
resourceManager.getRenderer().loadMap(copy, resourceManager.getMapTileDownloader());
}
else {
copy.increasePixelDimensions(copy.getPixWidth() / 3, copy.getPixHeight() / 4);
resourceManager.updateRendererMap(copy, null);
}
} }
} }
@ -174,7 +182,7 @@ public class MapVectorLayer extends BaseMapLayer {
private boolean drawRenderedMap(Canvas canvas, Bitmap bmp, RotatedTileBox bmpLoc, RotatedTileBox currentViewport) { private boolean drawRenderedMap(Canvas canvas, Bitmap bmp, RotatedTileBox bmpLoc, RotatedTileBox currentViewport) {
boolean shown = false; boolean shown = false;
if (bmp != null && bmpLoc != null) { if (bmp != null && bmpLoc != null) {
float rot = -bmpLoc.getRotate(); float rot = -bmpLoc.getRotate();
canvas.rotate(rot, currentViewport.getCenterPixelX(), currentViewport.getCenterPixelY()); canvas.rotate(rot, currentViewport.getCenterPixelX(), currentViewport.getCenterPixelY());
final RotatedTileBox calc = currentViewport.copy(); final RotatedTileBox calc = currentViewport.copy();

View file

@ -69,6 +69,10 @@ public class AsyncLoadingThread extends Thread {
} }
} }
public boolean areResourcesLoading() {
return !requests.isEmpty();
}
public void requestToLoadTile(TileLoadDownloadRequest req) { public void requestToLoadTile(TileLoadDownloadRequest req) {
requests.push(req); requests.push(req);
} }

View file

@ -1040,7 +1040,7 @@ public class ResourceManager {
return renderer.updateMapIsNeeded(rotatedTileBox, drawSettings); return renderer.updateMapIsNeeded(rotatedTileBox, drawSettings);
} }
public void updateRendererMap(RotatedTileBox rotatedTileBox, OnMapLoadedListener mapLoadedListener){ public synchronized void updateRendererMap(RotatedTileBox rotatedTileBox, OnMapLoadedListener mapLoadedListener){
renderer.interruptLoadingMap(); renderer.interruptLoadingMap();
asyncLoadingThread.requestToLoadMap(new MapLoadRequest(rotatedTileBox, mapLoadedListener)); asyncLoadingThread.requestToLoadMap(new MapLoadRequest(rotatedTileBox, mapLoadedListener));
} }
@ -1176,7 +1176,12 @@ public class ResourceManager {
tc.tilesOnFS.clear(); tc.tilesOnFS.clear();
} }
} }
public AsyncLoadingThread getAsyncLoadingThread() {
return asyncLoadingThread;
}
/// On low memory method /// /// On low memory method ///
public void onLowMemory() { public void onLowMemory() {
log.info("On low memory"); log.info("On low memory");

View file

@ -0,0 +1,164 @@
package net.osmand.plus.server;
import android.webkit.MimeTypeMap;
import fi.iki.elonen.NanoHTTPD;
import net.osmand.PlatformUtil;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.OsmandApplication;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.server.endpoints.TileEndpoint;
import java.io.IOException;
import java.io.InputStream;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
public class OsmAndHttpServer extends NanoHTTPD {
private static final String ASSETS_FOLDER_NAME = "server";
private static final org.apache.commons.logging.Log LOG = PlatformUtil.getLog(OsmAndHttpServer.class);
private final Map<String, ApiEndpoint> endpoints = new HashMap<>();
private MapActivity mapActivity;
private RotatedTileBox mapTileBoxCopy;
private boolean mapLinkedToLocation;
private OsmandApplication app;
public OsmAndHttpServer(String hostname, int port) {
super(hostname, port);
}
@Override
public Response serve(IHTTPSession session) {
String uri = session.getUri();
if (uri.equals("/")) {
return getStatic("/index.html");
}
if (isApiUrl(uri)) {
return routeApi(session);
}
return getStatic(uri);
}
public void start(MapActivity mapActivity) throws IOException {
this.mapActivity = mapActivity;
// don't leak map activity with applciation
this.app = mapActivity.getMyApplication();
registerEndpoints();
start(NanoHTTPD.SOCKET_READ_TIMEOUT, false);
mapActivity.getMapView().setScreenViewDetached(true);
this.mapTileBoxCopy = mapActivity.getMapView().getCurrentRotatedTileBox().copy();
mapLinkedToLocation = mapActivity.getMapViewTrackingUtilities().isMapLinkedToLocation();
mapActivity.getMapViewTrackingUtilities().setMapLinkedToLocation(false);
}
@Override
public void stop() {
mapActivity.getMapView().setScreenViewDetached(false);
mapActivity.getMapView().setCurrentViewport(mapTileBoxCopy);
mapActivity.getMapViewTrackingUtilities().setMapLinkedToLocation(mapLinkedToLocation);
mapActivity = null;
super.stop();
}
public MapActivity getMapActivity() {
return mapActivity;
}
public OsmandApplication getMyApplication() {
return app;
}
public String getUrl() {
RotatedTileBox rtb = mapActivity.getMapView().getCurrentRotatedTileBox();
float lat = (float) rtb.getLatitude();
float lon = (float) rtb.getLongitude();
int z = rtb.getZoom();
return String.format("http://%s:%d/?lat=%.4f&lon%.4f&zoom=%d", getHostname(), getListeningPort(),
lat, lon, z);
}
private NanoHTTPD.Response routeApi(NanoHTTPD.IHTTPSession session) {
String uri = session.getUri();
Iterator<Map.Entry<String, ApiEndpoint>> it = endpoints.entrySet().iterator();
while (it.hasNext()) {
Map.Entry<String, ApiEndpoint> e = it.next();
if (uri.startsWith(e.getKey())) {
try {
return e.getValue().process(session, uri);
} catch (Exception exception) {
LOG.error("SERVER ERROR: " + exception.getMessage());
return ErrorResponses.response500;
}
}
}
return ErrorResponses.response404;
}
private boolean isApiUrl(String uri) {
for (String endpoint : endpoints.keySet()) {
int stringLength = endpoint.length();
if (uri.startsWith(endpoint) &&
(uri.length() == endpoint.length() || uri.charAt(stringLength) == '/')) {
return true;
}
}
return false;
}
private void registerEndpoints() {
register("/tile", new TileEndpoint(this));
}
private void register(String path, ApiEndpoint endpoint) {
endpoints.put(path, endpoint);
}
private NanoHTTPD.Response getStatic(String uri) {
InputStream is;
String mimeType = parseMimeType(uri);
OsmandApplication app = mapActivity.getMyApplication();
if (app != null) {
try {
is = app.getAssets().open(ASSETS_FOLDER_NAME + uri);
if (is.available() == 0) {
return ErrorResponses.response404;
}
return newFixedLengthResponse(
NanoHTTPD.Response.Status.OK,
mimeType,
is,
is.available());
} catch (IOException e) {
LOG.error(e);
return ErrorResponses.response404;
}
}
return ErrorResponses.response500;
}
private String parseMimeType(String url) {
String type = "text/plain";
if (url.endsWith(".js")) return "text/javascript";
String extension = MimeTypeMap.getFileExtensionFromUrl(url);
if (extension != null) {
type = MimeTypeMap.getSingleton().getMimeTypeFromExtension(extension);
}
return type;
}
public interface ApiEndpoint {
NanoHTTPD.Response process(IHTTPSession session, String url);
}
public static class ErrorResponses {
public static NanoHTTPD.Response response404 =
newFixedLengthResponse(NanoHTTPD.Response.Status.NOT_FOUND,
NanoHTTPD.MIME_PLAINTEXT, "404 Not Found");
public static NanoHTTPD.Response response500 =
newFixedLengthResponse(NanoHTTPD.Response.Status.INTERNAL_ERROR,
NanoHTTPD.MIME_PLAINTEXT, "500 Internal Server Error");
}
}

View file

@ -0,0 +1,126 @@
package net.osmand.plus.server;
import android.net.TrafficStats;
import android.net.wifi.WifiManager;
import android.os.Bundle;
import android.os.StrictMode;
import android.text.format.Formatter;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import android.widget.Toast;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import fi.iki.elonen.NanoHTTPD;
import net.osmand.PlatformUtil;
import net.osmand.plus.R;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.base.BaseOsmAndFragment;
import org.apache.commons.logging.Log;
import java.io.IOException;
import static android.content.Context.WIFI_SERVICE;
public class ServerFragment extends BaseOsmAndFragment {
public static final String TAG = "ServerFrag";
private final static Log LOG = PlatformUtil.getLog(ServerFragment.class);
private final int port = 24990;
final int THREAD_ID = 14231; // random number
private boolean initialized = false;
private OsmAndHttpServer server;
private View view;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
enableStrictMode();
super.onCreate(savedInstanceState);
}
@Override
public View onCreateView(@NonNull LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
view = inflater.inflate(R.layout.server_fragment, container, false);
view.findViewById(R.id.server_start_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
if (!initialized) {
updateTextView(getString(R.string.stop_web_server));
initServer();
}
}
});
view.findViewById(R.id.server_stop_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View view) {
updateTextView(getString(R.string.start_web_server));
deInitServer();
}
});
return view;
}
@Override
public void onDestroy() {
deInitServer();
super.onDestroy();
}
public static void enableStrictMode() {
StrictMode.setThreadPolicy(
new StrictMode.ThreadPolicy.Builder()
.detectDiskReads()
.detectDiskWrites()
.detectNetwork()
.permitDiskReads()
.permitDiskWrites()
.penaltyLog()
.build());
StrictMode.setVmPolicy(
new StrictMode.VmPolicy.Builder()
.detectLeakedSqlLiteObjects()
.penaltyLog()
.build());
}
private void updateTextView(String text) {
((TextView) view.findViewById(R.id.server_status_textview)).setText(text);
}
private void initServer() {
TrafficStats.setThreadStatsTag(THREAD_ID);
String hostname = getDeviceAddress();
try {
server = new OsmAndHttpServer(hostname, port);
server.start((MapActivity) getActivity());
initialized = true;
updateTextView("Server started at " + server.getUrl());
} catch (IOException e) {
Toast.makeText(requireContext(),
e.getLocalizedMessage(),
Toast.LENGTH_SHORT).show();
LOG.error(e);
}
}
private String getDeviceAddress() {
WifiManager wm = (WifiManager) requireContext().getSystemService(WIFI_SERVICE);
String ip = Formatter.formatIpAddress(wm.getConnectionInfo().getIpAddress());
return ip != null ? ip : "0.0.0.0";
}
private void deInitServer() {
if (server != null) {
server.closeAllConnections();
server.stop();
}
initialized = false;
if (getActivity() != null) {
try {
getActivity().getSupportFragmentManager().beginTransaction().remove(this).commit();
} catch (RuntimeException e) {
LOG.error(e.getMessage(), e);
}
}
}
}

View file

@ -0,0 +1,162 @@
package net.osmand.plus.server.endpoints;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import net.osmand.PlatformUtil;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.OsmandApplication;
import net.osmand.util.MapUtils;
import org.apache.commons.logging.Log;
import java.io.File;
import java.io.FileOutputStream;
import java.util.concurrent.ConcurrentLinkedQueue;
public class MetaTileFileSystemCache {
private static final Log LOG = PlatformUtil.getLog(TileEndpoint.class);
private static final String TILES_FOLDER = "webtiles";
static final int TILE_SIZE_PX = 256;
static final int TILE_DENSITY = 2;
static final int METATILE_SIZE = 2;
private static final int MAX_IN_MEMORY_CACHE_SIZE = 16 / METATILE_SIZE;
private static final int MAX_CACHE_SIZE = 128;
private final ConcurrentLinkedQueue<MetaTileCache> inMemoryCache = new ConcurrentLinkedQueue<>();
private final File externalCacheDir;
public MetaTileFileSystemCache(OsmandApplication application) {
externalCacheDir = application.getAppPath(TILES_FOLDER);
if (!externalCacheDir.exists()) {
externalCacheDir.mkdir();
}
}
public void put(MetaTileCache tile) {
// TODO list files too slow, better to have local variable to monitor or local list
while (externalCacheDir.listFiles().length > MAX_CACHE_SIZE) {
for (int i = 0; i < externalCacheDir.listFiles().length - MAX_CACHE_SIZE; i++) {
externalCacheDir.listFiles()[i].delete();
}
}
String fileName = tile.getTileId();
File file = new File(externalCacheDir, fileName);
if (file.exists()) {
file.delete();
}
try {
FileOutputStream out = new FileOutputStream(file);
tile.bmp.compress(Bitmap.CompressFormat.PNG, 100, out);
out.flush();
out.close();
} catch (Exception e) {
LOG.error(e.getMessage(), e);
}
if (reserveMemSlot()) {
inMemoryCache.add(tile);
}
}
private boolean reserveMemSlot() {
while (MAX_IN_MEMORY_CACHE_SIZE > 0 && inMemoryCache.size() >= MAX_IN_MEMORY_CACHE_SIZE) {
inMemoryCache.poll();
}
return MAX_IN_MEMORY_CACHE_SIZE > 0;
}
public MetaTileCache get(int zoom, int x, int y) {
int mx = (x / METATILE_SIZE) * METATILE_SIZE;
int my = (y / METATILE_SIZE) * METATILE_SIZE;
for (MetaTileCache r : inMemoryCache) {
if (r.zoom == zoom && r.ex >= x && r.ey >= y && r.sx <= x && r.sy <= y) {
return r;
}
}
File file = new File(externalCacheDir, zoom + "_" + METATILE_SIZE + "_" + mx + "_" + my);
if (file.exists()) {
MetaTileCache tile = new MetaTileCache(
BitmapFactory.decodeFile(file.getAbsolutePath()),
mx, my, mx + METATILE_SIZE - 1, my + METATILE_SIZE - 1, zoom);
if (reserveMemSlot()) {
inMemoryCache.add(tile);
}
return tile;
}
return null;
}
public void clearCache() {
clearInMemoryCache();
clearFileCache();
}
private void clearFileCache() {
for (int i = 0; i < externalCacheDir.listFiles().length; i++) {
externalCacheDir.listFiles()[i].delete();
}
}
private void clearInMemoryCache() {
inMemoryCache.clear();
}
public MetaTileCache createMetaTile(int zoom, int x, int y) {
int mx = (x / METATILE_SIZE) * METATILE_SIZE;
int my = (y / METATILE_SIZE) * METATILE_SIZE;
double lat = MapUtils.getLatitudeFromTile(zoom, my + 0.5 * METATILE_SIZE);
double lon = MapUtils.getLongitudeFromTile(zoom, mx + 0.5 * METATILE_SIZE);
MetaTileCache res = new MetaTileCache();
res.bbox = new RotatedTileBox.RotatedTileBoxBuilder()
.setLocation(lat, lon)
.setMapDensity(TILE_DENSITY).density(TILE_DENSITY)
.setZoom(zoom)
.setPixelDimensions(TILE_SIZE_PX * TILE_DENSITY * METATILE_SIZE,
TILE_SIZE_PX * TILE_DENSITY * METATILE_SIZE, 0.5f, 0.5f).build();
res.sx = mx;
res.ex = mx + METATILE_SIZE - 1;
res.sy = my;
res.ey = my + METATILE_SIZE - 1;
res.zoom = zoom;
return res;
}
public static class MetaTileCache {
Bitmap bmp;
int sx;
int sy;
int ex;
int ey;
int zoom;
public RotatedTileBox bbox;
public MetaTileCache() {
}
public MetaTileCache(Bitmap bmp, int sx, int sy, int ex, int ey, int zoom) {
this.bmp = bmp;
this.sx = sx;
this.sy = sy;
this.ex = ex;
this.ey = ey;
this.zoom = zoom;
}
// to be used in file name
public String getTileId() {
return zoom + "_" + METATILE_SIZE + "_" + sx + "_" + sy;
}
public Bitmap getBitmap() {
return bmp;
}
public Bitmap getSubtile(int x, int y) {
return Bitmap.createBitmap(bmp,
(x - sx) * TILE_SIZE_PX * TILE_DENSITY,
(y - sy) * TILE_SIZE_PX * TILE_DENSITY,
TILE_SIZE_PX * TILE_DENSITY, TILE_SIZE_PX * TILE_DENSITY);
}
}
}

View file

@ -0,0 +1,120 @@
package net.osmand.plus.server.endpoints;
import android.graphics.Bitmap;
import fi.iki.elonen.NanoHTTPD;
import net.osmand.PlatformUtil;
import net.osmand.data.RotatedTileBox;
import net.osmand.plus.activities.MapActivity;
import net.osmand.plus.resources.ResourceManager;
import net.osmand.plus.server.OsmAndHttpServer;
import net.osmand.plus.views.OsmandMapLayer;
import org.apache.commons.logging.Log;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import static fi.iki.elonen.NanoHTTPD.newFixedLengthResponse;
public class TileEndpoint implements OsmAndHttpServer.ApiEndpoint {
private static final int TIMEOUT_STEP = 150;
private static final int TIMEOUT = 15000;
private static final Log LOG = PlatformUtil.getLog(TileEndpoint.class);
private final RotatedTileBox mapTileBoxCopy;
private final OsmAndHttpServer server;
private final MetaTileFileSystemCache cache;
private int lastRequestedZoom;
public TileEndpoint(OsmAndHttpServer server) {
this.server = server;
this.cache = new MetaTileFileSystemCache(server.getMyApplication());
this.mapTileBoxCopy = server.getMapActivity().getMapView().getCurrentRotatedTileBox().copy();
this.cache.clearCache();
}
@Override
public NanoHTTPD.Response process(NanoHTTPD.IHTTPSession session, String url) {
// https://tile.osmand.net/hd/6/55/25.png
int extInd = url.indexOf('.');
if (extInd >= 0) {
url = url.substring(0, extInd);
}
if (url.charAt(0) == '/') {
url = url.substring(1);
}
String[] prms = url.split("/");
if (prms.length < 4) {
return OsmAndHttpServer.ErrorResponses.response500;
}
int zoom = Integer.parseInt(prms[1]);
int x = Integer.parseInt(prms[2]);
int y = Integer.parseInt(prms[3]);
MetaTileFileSystemCache.MetaTileCache res = cache.get(zoom, x, y);
if (res == null) {
synchronized (this) {
lastRequestedZoom = zoom;
}
res = requestMetatile(x, y, zoom);
if (res == null) {
return OsmAndHttpServer.ErrorResponses.response500;
}
}
ByteArrayOutputStream stream = new ByteArrayOutputStream();
Bitmap bmp = res.getSubtile(x, y);
if (bmp == null) {
LOG.error("SERVER: Cannot cut bitmap");
return OsmAndHttpServer.ErrorResponses.response500;
}
bmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
byte[] byteArray = stream.toByteArray();
ByteArrayInputStream str = new ByteArrayInputStream(byteArray);
return newFixedLengthResponse(
NanoHTTPD.Response.Status.OK, "image/png",
str, str.available());
}
private synchronized MetaTileFileSystemCache.MetaTileCache requestMetatile(int x, int y, int zoom) {
long tm = System.currentTimeMillis();
MapActivity mapActivity = server.getMapActivity();
ResourceManager resourceManager = mapActivity.getMyApplication().getResourceManager();
if (mapActivity == null) {
return null;
}
MetaTileFileSystemCache.MetaTileCache cacheTile = this.cache.get(zoom, x, y);
if (cacheTile != null) {
return cacheTile;
}
MetaTileFileSystemCache.MetaTileCache res = cache.createMetaTile(zoom, x, y);
mapActivity.getMapView().setCurrentViewport(res.bbox);
int timeout = 0;
try {
LOG.debug("SERVER: interrupt in request");
resourceManager.getRenderer().loadMap(res.bbox, resourceManager.getMapTileDownloader());
Thread.sleep(TIMEOUT_STEP); // to do line should be removed in future
OsmandMapLayer.DrawSettings drawSettings =
new OsmandMapLayer.DrawSettings(mapActivity.getMapView().getSettings().isLightContent(),
true);
mapActivity.getMapView().refreshMapInternal(drawSettings);
mapActivity.getMapView().refreshBaseMapInternal(res.bbox, drawSettings);
Thread.sleep(TIMEOUT_STEP); // to do line should be removed in future
// wait for image to be refreshed
while (!resourceManager.getRenderingBufferImageThread().getLooper().getQueue().isIdle() &&
timeout < TIMEOUT) {
Thread.sleep(TIMEOUT_STEP);
timeout += TIMEOUT_STEP;
}
Bitmap rbmp = mapActivity.getMapView().getBufferBitmap();
res.bmp = rbmp.copy(rbmp.getConfig(), true);
this.cache.put(res);
LOG.debug("SERVER: metatile: " + (System.currentTimeMillis() - tm) / 1000.0f);
return res;
} catch (Exception e) {
LOG.error(e);
}
LOG.debug("SERVER: metatile1: " + (System.currentTimeMillis() - tm) / 1000.0f);
return null;
}
}

View file

@ -77,6 +77,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
protected OsmandSettings settings = null; protected OsmandSettings settings = null;
private CanvasColors canvasColors = null; private CanvasColors canvasColors = null;
private Boolean nightMode = null; private Boolean nightMode = null;
private boolean isScreenViewDetached = false;
private class CanvasColors { private class CanvasColors {
int colorDay = MAP_DEFAULT_COLOR; int colorDay = MAP_DEFAULT_COLOR;
@ -283,6 +284,14 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
return layers.contains(layer); return layers.contains(layer);
} }
public boolean isScreenViewDetached() {
return isScreenViewDetached;
}
public void setScreenViewDetached(boolean screenViewDetached) {
isScreenViewDetached = screenViewDetached;
}
public float getZorder(OsmandMapLayer layer) { public float getZorder(OsmandMapLayer layer) {
Float z = zOrders.get(layer); Float z = zOrders.get(layer);
if (z == null) { if (z == null) {
@ -579,7 +588,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
} }
} }
private void refreshBaseMapInternal(RotatedTileBox tileBox, DrawSettings drawSettings) { public void refreshBaseMapInternal(RotatedTileBox tileBox, DrawSettings drawSettings) {
if (tileBox.getPixHeight() == 0 || tileBox.getPixWidth() == 0) { if (tileBox.getPixHeight() == 0 || tileBox.getPixWidth() == 0) {
return; return;
} }
@ -616,7 +625,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
additional.calculateFPS(start, end); additional.calculateFPS(start, end);
} }
private void refreshMapInternal(DrawSettings drawSettings) { public void refreshMapInternal(DrawSettings drawSettings) {
if (view == null) { if (view == null) {
return; return;
} }
@ -643,9 +652,9 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
} }
final int cy = (int) (ratioy * view.getHeight()); final int cy = (int) (ratioy * view.getHeight());
final int cx = (int) (ratiox * view.getWidth()); final int cx = (int) (ratiox * view.getWidth());
if (currentViewport.getPixWidth() != view.getWidth() || currentViewport.getPixHeight() != view.getHeight() || if ((currentViewport.getPixWidth() != view.getWidth() || currentViewport.getPixHeight() != view.getHeight() ||
currentViewport.getCenterPixelY() != cy || currentViewport.getCenterPixelY() != cy ||
currentViewport.getCenterPixelX() != cx) { currentViewport.getCenterPixelX() != cx) && !isScreenViewDetached) {
currentViewport.setPixelDimensions(view.getWidth(), view.getHeight(), ratiox, ratioy); currentViewport.setPixelDimensions(view.getWidth(), view.getHeight(), ratiox, ratioy);
refreshBufferImage(drawSettings); refreshBufferImage(drawSettings);
} }
@ -779,7 +788,7 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
canvas.drawCircle(x, y, 7 * dm.density, paintCenter); canvas.drawCircle(x, y, 7 * dm.density, paintCenter);
} }
private void refreshBufferImage(final DrawSettings drawSettings) { public void refreshBufferImage(final DrawSettings drawSettings) {
if (mapRenderer != null) { if (mapRenderer != null) {
return; return;
} }
@ -821,17 +830,17 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
// this method could be called in non UI thread // this method could be called in non UI thread
public void refreshMap(final boolean updateVectorRendering) { public void refreshMap(final boolean updateVectorRendering) {
if (view != null && view.isShown()) { if ((view != null && view.isShown()) || isScreenViewDetached) {
boolean nightMode = application.getDaynightHelper().isNightMode(); // boolean nightMode = application.getDaynightHelper().isNightMode();
Boolean currentNightMode = this.nightMode; // Boolean currentNightMode = this.nightMode;
boolean forceUpdateVectorDrawing = currentNightMode != null && currentNightMode != nightMode; // boolean forceUpdateVectorDrawing = currentNightMode != null && currentNightMode != nightMode;
if (forceUpdateVectorDrawing) { // if (forceUpdateVectorDrawing) {
resetDefaultColor(); // resetDefaultColor();
} // }
this.nightMode = nightMode; // this.nightMode = nightMode;
DrawSettings drawSettings = new DrawSettings(nightMode, updateVectorRendering || forceUpdateVectorDrawing); // DrawSettings drawSettings = new DrawSettings(nightMode, updateVectorRendering || forceUpdateVectorDrawing);
sendRefreshMapMsg(drawSettings, 20); // sendRefreshMapMsg(drawSettings, 20);
refreshBufferImage(drawSettings); // refreshBufferImage(drawSettings);
} }
} }
@ -869,6 +878,19 @@ public class OsmandMapTileView implements IMapDownloaderCallback {
return currentViewport; return currentViewport;
} }
public void setCurrentViewport(RotatedTileBox viewport) {
currentViewport = viewport;
//refreshMap(true);
}
public Bitmap getBufferBitmap() {
return bufferBitmap;
}
public RotatedTileBox getBufferImgLoc() {
return bufferImgLoc;
}
public float getDensity() { public float getDensity() {
return currentViewport.getDensity(); return currentViewport.getDensity();
} }