Compare commits
70 commits
master
...
android_ht
Author | SHA1 | Date | |
---|---|---|---|
|
419edcac29 | ||
|
b5a24dc23b | ||
|
9a35b70773 | ||
|
31a3ec3b05 | ||
|
dcc05f7129 | ||
|
e070f5be94 | ||
|
b708f8e6ff | ||
|
65e8328a0c | ||
|
1d3849ef4d | ||
|
98f0823bbb | ||
|
ff236c7d81 | ||
|
d62a92b5e8 | ||
|
c914e18fc1 | ||
|
1b85b5a732 | ||
|
ad82cd419f | ||
|
9d37cff564 | ||
|
d0c6270a00 | ||
|
6c86997d2c | ||
|
2cd4e23463 | ||
|
576e70a606 | ||
|
ce2b0eacbf | ||
|
2851fd38cd | ||
|
463679afd3 | ||
|
ad444aadaf | ||
|
2442759d07 | ||
|
b779b8b61f | ||
|
f34b1fdf11 | ||
|
a92bac55f7 | ||
|
96653b3749 | ||
|
ee54d374ff | ||
|
2b9db97fb7 | ||
|
b3cb492558 | ||
|
08ab7a6613 | ||
|
150e769709 | ||
|
485ee88a13 | ||
|
aeff02d633 | ||
|
b4d4e79b6a | ||
|
cdde9f3960 | ||
|
23b871ee22 | ||
|
7ae7f45c48 | ||
|
cb9c141c64 | ||
|
9ed5eb95c7 | ||
|
aa1932e3d2 | ||
|
fbeba45ada | ||
|
9fc7d9ef26 | ||
|
96ad964beb | ||
|
920241a043 | ||
|
8505f67d68 | ||
|
8dd5bda3fc | ||
|
9112d4a34f | ||
|
edb3aede9e | ||
|
a7633b2031 | ||
|
fef6f93621 | ||
|
9823461435 | ||
|
d1451e9889 | ||
|
c436b1dcb8 | ||
|
b846969e49 | ||
|
2f1707167c | ||
|
656a316f87 | ||
|
c81e5a88ae | ||
|
2869cae3a0 | ||
|
1f8dbb7694 | ||
|
71c895c81e | ||
|
40bc660320 | ||
|
c0c07b95c2 | ||
|
065755d5b2 | ||
|
8f4b8c8349 | ||
|
3ad4e5fc71 | ||
|
409aa0574a | ||
|
c2395016f9 |
22 changed files with 4689 additions and 24 deletions
|
@ -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";
|
||||||
|
|
3322
OsmAnd/assets/server/css/site.css
Normal file
3322
OsmAnd/assets/server/css/site.css
Normal file
File diff suppressed because it is too large
Load diff
BIN
OsmAnd/assets/server/images/logo-grey.png
Normal file
BIN
OsmAnd/assets/server/images/logo-grey.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 4.4 KiB |
81
OsmAnd/assets/server/index.html
Normal file
81
OsmAnd/assets/server/index.html
Normal 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>
|
273
OsmAnd/assets/server/scripts/go.js
Normal file
273
OsmAnd/assets/server/scripts/go.js
Normal 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':'© <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();
|
||||||
|
});
|
4
OsmAnd/assets/server/scripts/jquery-3.1.0.min.js
vendored
Normal file
4
OsmAnd/assets/server/scripts/jquery-3.1.0.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
165
OsmAnd/assets/server/scripts/js.cookie.js
Normal file
165
OsmAnd/assets/server/scripts/js.cookie.js
Normal 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 () {});
|
||||||
|
}));
|
134
OsmAnd/assets/server/scripts/leaflet.awesome-markers.js
Normal file
134
OsmAnd/assets/server/scripts/leaflet.awesome-markers.js
Normal 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));
|
||||||
|
|
||||||
|
|
||||||
|
|
7
OsmAnd/assets/server/scripts/leaflet.awesome-markers.min.js
vendored
Normal file
7
OsmAnd/assets/server/scripts/leaflet.awesome-markers.min.js
vendored
Normal 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);
|
|
@ -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'
|
||||||
|
|
34
OsmAnd/res/layout/server_fragment.xml
Normal file
34
OsmAnd/res/layout/server_fragment.xml
Normal 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>
|
|
@ -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>
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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()) {
|
||||||
|
|
|
@ -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();
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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");
|
||||||
|
|
164
OsmAnd/src/net/osmand/plus/server/OsmAndHttpServer.java
Normal file
164
OsmAnd/src/net/osmand/plus/server/OsmAndHttpServer.java
Normal 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");
|
||||||
|
}
|
||||||
|
}
|
126
OsmAnd/src/net/osmand/plus/server/ServerFragment.java
Normal file
126
OsmAnd/src/net/osmand/plus/server/ServerFragment.java
Normal 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
120
OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java
Normal file
120
OsmAnd/src/net/osmand/plus/server/endpoints/TileEndpoint.java
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue