mirror of
https://github.com/sunnypilot/sunnypilot.git
synced 2026-08-06 21:55:41 +08:00
@@ -0,0 +1,420 @@
|
||||
#!/usr/bin/env python3.8
|
||||
# The MIT License
|
||||
#
|
||||
# Copyright (c) 2019-, Rick Lan, dragonpilot community, and a number of other of contributors.
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in
|
||||
# all copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
# THE SOFTWARE.
|
||||
|
||||
from http.server import BaseHTTPRequestHandler, HTTPServer
|
||||
from cgi import parse_header, parse_multipart
|
||||
from urllib.parse import parse_qs, unquote
|
||||
import json
|
||||
import requests
|
||||
import math
|
||||
from common.basedir import BASEDIR
|
||||
from common.params import Params
|
||||
params = Params()
|
||||
|
||||
hostName = ""
|
||||
serverPort = 8082
|
||||
|
||||
pi = 3.1415926535897932384626
|
||||
x_pi = 3.14159265358979324 * 3000.0 / 180.0
|
||||
a = 6378245.0
|
||||
ee = 0.00669342162296594323
|
||||
|
||||
|
||||
class OtisServ(BaseHTTPRequestHandler):
|
||||
def do_GET(self):
|
||||
use_amap = params.get_bool("EnableAmap")
|
||||
use_gmap = not use_amap and params.get_bool("EnableGmap")
|
||||
|
||||
if self.path == '/logo.png':
|
||||
self.get_logo()
|
||||
return
|
||||
if self.path == '/?reset=1':
|
||||
params.put("NavDestination", "")
|
||||
if use_amap:
|
||||
if self.path == '/style.css':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/css")
|
||||
self.end_headers()
|
||||
self.get_amap_css()
|
||||
return
|
||||
elif self.path == '/index.js':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/javascript")
|
||||
self.end_headers()
|
||||
self.get_amap_js()
|
||||
return
|
||||
else:
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
if self.get_amap_key() is None or self.get_amap_key_2() is None:
|
||||
self.display_page_amap_key()
|
||||
return
|
||||
if self.get_app_token() is None:
|
||||
self.display_page_app_token()
|
||||
return
|
||||
self.display_page_amap()
|
||||
elif use_gmap:
|
||||
if self.path == '/style.css':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/css")
|
||||
self.end_headers()
|
||||
self.get_gmap_css()
|
||||
return
|
||||
elif self.path == '/index.js':
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/javascript")
|
||||
self.end_headers()
|
||||
self.get_gmap_js()
|
||||
return
|
||||
else:
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
if self.get_gmap_key() is None:
|
||||
self.display_page_gmap_key()
|
||||
return
|
||||
if self.get_app_token() is None:
|
||||
self.display_page_app_token()
|
||||
return
|
||||
self.display_page_gmap()
|
||||
else:
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
if self.get_public_token() is None:
|
||||
self.display_page_public_token()
|
||||
return
|
||||
if self.get_app_token() is None:
|
||||
self.display_page_app_token()
|
||||
return
|
||||
self.display_page_addr_input()
|
||||
|
||||
def do_POST(self):
|
||||
use_amap = params.get_bool("EnableAmap")
|
||||
use_gmap = not use_amap and params.get_bool("EnableGmap")
|
||||
|
||||
postvars = self.parse_POST()
|
||||
self.send_response(200)
|
||||
self.send_header("Content-type", "text/html")
|
||||
self.end_headers()
|
||||
|
||||
if use_amap:
|
||||
# amap token
|
||||
if self.get_amap_key() is None or self.get_amap_key_2() is None:
|
||||
if postvars is None or \
|
||||
("amap_key_val" not in postvars or postvars.get("amap_key_val")[0] == "") or \
|
||||
("amap_key_val_2" not in postvars or postvars.get("amap_key_val_2")[0] == ""):
|
||||
self.display_page_amap_key()
|
||||
return
|
||||
params.put("AmapKey1", postvars.get("amap_key_val")[0])
|
||||
params.put("AmapKey2", postvars.get("amap_key_val_2")[0])
|
||||
|
||||
elif use_gmap:
|
||||
# gmap token
|
||||
if self.get_gmap_key() is None:
|
||||
if postvars is None or "gmap_key_val" not in postvars or postvars.get("gmap_key_val")[0] == "":
|
||||
self.display_page_gmap_key()
|
||||
return
|
||||
params.put("GmapKey", postvars.get("gmap_key_val")[0])
|
||||
|
||||
else:
|
||||
# mapbox public key
|
||||
if self.get_public_token() is None:
|
||||
if postvars is None or "pk_token_val" not in postvars or postvars.get("pk_token_val")[0] == "":
|
||||
self.display_page_public_token()
|
||||
return
|
||||
token = postvars.get("pk_token_val")[0]
|
||||
if "pk." not in token:
|
||||
self.display_page_public_token("Your token was incorrect!")
|
||||
return
|
||||
params.put('CustomMapboxTokenPk', token)
|
||||
|
||||
# app key
|
||||
if self.get_app_token() is None:
|
||||
if postvars is None or "sk_token_val" not in postvars or postvars.get("sk_token_val")[0] == "":
|
||||
self.display_page_app_token()
|
||||
return
|
||||
token = postvars.get("sk_token_val")[0]
|
||||
if "sk." not in token:
|
||||
self.display_page_app_token("Your token was incorrect!")
|
||||
return
|
||||
params.put('CustomMapboxTokenSk', token)
|
||||
|
||||
# nav confirmed
|
||||
if postvars is not None:
|
||||
if "lat" in postvars and postvars.get("lat")[0] != "" and "lon" in postvars and postvars.get("lon")[0] != "":
|
||||
lat = float(postvars.get("lat")[0])
|
||||
lng = float(postvars.get("lon")[0])
|
||||
save_type = postvars.get("save_type")[0]
|
||||
name = postvars.get("name")[0] if postvars.get("name") is not None else ""
|
||||
if use_amap:
|
||||
lng, lat = self.gcj02towgs84(lng, lat)
|
||||
params.put('NavDestination', "{\"latitude\": %f, \"longitude\": %f, \"place_name\": \"%s\"}" % (lat, lng, name))
|
||||
self.to_json(lat, lng, save_type, name)
|
||||
# favorites
|
||||
if not use_gmap and "fav_val" in postvars:
|
||||
addr = postvars.get("fav_val")[0]
|
||||
real_addr = None
|
||||
lon = None
|
||||
lat = None
|
||||
if addr != "favorites":
|
||||
val = params.get("ApiCache_NavDestinations", encoding='utf8')
|
||||
if val is not None:
|
||||
val = val.rstrip('\x00')
|
||||
dests = json.loads(val)
|
||||
for item in dests:
|
||||
if "label" in item and item["label"] == addr:
|
||||
lat = item["latitude"]
|
||||
lon = item["longitude"]
|
||||
real_addr = item["place_name"]
|
||||
break
|
||||
else:
|
||||
real_addr = None
|
||||
if real_addr is not None:
|
||||
self.display_page_nav_confirmation(real_addr, lon, lat)
|
||||
return
|
||||
else:
|
||||
self.display_page_addr_input("Place Not Found")
|
||||
return
|
||||
# search
|
||||
if not use_gmap and "addr_val" in postvars:
|
||||
addr = postvars.get("addr_val")[0]
|
||||
if addr != "":
|
||||
real_addr, lat, lon = self.query_addr(addr)
|
||||
if real_addr is not None:
|
||||
self.display_page_nav_confirmation(real_addr, lon, lat)
|
||||
return
|
||||
else:
|
||||
self.display_page_addr_input("Place Not Found")
|
||||
return
|
||||
if use_amap:
|
||||
self.display_page_amap()
|
||||
elif use_gmap:
|
||||
self.display_page_gmap()
|
||||
else:
|
||||
self.display_page_addr_input()
|
||||
|
||||
def get_logo(self):
|
||||
self.send_response(200)
|
||||
self.send_header('Content-type','image/png')
|
||||
self.end_headers()
|
||||
f = open("%s/selfdrive/assets/img_spinner_comma.png" % BASEDIR, "rb")
|
||||
self.wfile.write(f.read())
|
||||
f.close()
|
||||
|
||||
def get_gmap_css(self):
|
||||
self.wfile.write(bytes(self.get_parsed_template("gmap/style.css"), "utf-8"))
|
||||
|
||||
def get_gmap_js(self):
|
||||
lon, lat = self.get_last_lon_lat()
|
||||
self.wfile.write(bytes(self.get_parsed_template("gmap/index.js", {"{{lat}}": lat, "{{lon}}": lon}), "utf-8"))
|
||||
|
||||
def get_gmap_key(self):
|
||||
token = params.get("GmapKey", encoding='utf8')
|
||||
if token is not None and token != "":
|
||||
return token.rstrip('\x00')
|
||||
return None
|
||||
|
||||
def get_amap_css(self):
|
||||
self.wfile.write(bytes(self.get_parsed_template("amap/style.css"), "utf-8"))
|
||||
|
||||
def get_amap_js(self):
|
||||
lon, lat = self.get_last_lon_lat()
|
||||
self.wfile.write(bytes(self.get_parsed_template("amap/index.js", {"{{lat}}": lat, "{{lon}}": lon}), "utf-8"))
|
||||
|
||||
def get_amap_key(self):
|
||||
token = params.get("AmapKey1", encoding='utf8')
|
||||
if token is not None and token != "":
|
||||
return token.rstrip('\x00')
|
||||
return None
|
||||
|
||||
def get_amap_key_2(self):
|
||||
token = params.get("AmapKey2", encoding='utf8')
|
||||
if token is not None and token != "":
|
||||
return token.rstrip('\x00')
|
||||
return None
|
||||
|
||||
def get_public_token(self):
|
||||
token = params.get("CustomMapboxTokenPk", encoding='utf8')
|
||||
if token is not None and token != "":
|
||||
return token.rstrip('\x00')
|
||||
return None
|
||||
|
||||
def get_app_token(self):
|
||||
token = params.get("CustomMapboxTokenSk", encoding='utf8')
|
||||
if token is not None and token != "":
|
||||
return token.rstrip('\x00')
|
||||
return None
|
||||
|
||||
def get_last_lon_lat(self):
|
||||
last_pos = Params().get("LastGPSPosition")
|
||||
if last_pos is not None and last_pos != "":
|
||||
l = json.loads(last_pos)
|
||||
return l["longitude"], l["latitude"]
|
||||
return "", ""
|
||||
|
||||
def display_page_gmap_key(self):
|
||||
self.wfile.write(bytes(self.get_parsed_template("body", {"{{content}}": self.get_parsed_template("gmap/key_input")}), "utf-8"))
|
||||
|
||||
def display_page_amap_key(self):
|
||||
self.wfile.write(bytes(self.get_parsed_template("body", {"{{content}}": self.get_parsed_template("amap/key_input")}), "utf-8"))
|
||||
|
||||
def display_page_public_token(self, msg = ""):
|
||||
self.wfile.write(bytes(self.get_parsed_template("body", {"{{content}}": self.get_parsed_template("public_token_input", {"{{msg}}": msg})}), "utf-8"))
|
||||
|
||||
def display_page_app_token(self, msg = ""):
|
||||
self.wfile.write(bytes(self.get_parsed_template("body", {"{{content}}": self.get_parsed_template("app_token_input", {"{{msg}}": msg})}), "utf-8"))
|
||||
|
||||
def display_page_addr_input(self, msg = ""):
|
||||
self.wfile.write(bytes(self.get_parsed_template("body", {"{{content}}": self.get_parsed_template("addr_input", {"{{msg}}": msg})}), "utf-8"))
|
||||
|
||||
def display_page_nav_confirmation(self, addr, lon, lat):
|
||||
content = self.get_parsed_template("addr_input", {"{{msg}}": ""}) + self.get_parsed_template("nav_confirmation", {"{{token}}": self.get_public_token(), "{{lon}}": lon, "{{lat}}": lat, "{{addr}}": addr})
|
||||
self.wfile.write(bytes(self.get_parsed_template("body", {"{{content}}": content }), "utf-8"))
|
||||
|
||||
def display_page_gmap(self):
|
||||
self.wfile.write(bytes(self.get_parsed_template("gmap/index.html", {"{{gmap_key}}": self.get_gmap_key()}), "utf-8"))
|
||||
|
||||
def display_page_amap(self):
|
||||
self.wfile.write(bytes(self.get_parsed_template("amap/index.html", {"{{amap_key}}": self.get_amap_key(), "{{amap_key_2}}": self.get_amap_key_2()}), "utf-8"))
|
||||
|
||||
def get_parsed_template(self, name, replace = {}):
|
||||
f = open('%s/selfdrive/navd/tpl/%s.tpl' % (BASEDIR, name), mode='r', encoding='utf-8')
|
||||
content = f.read()
|
||||
for key in replace:
|
||||
content = content.replace(key, str(replace[key]))
|
||||
f.close()
|
||||
return content
|
||||
|
||||
def query_addr(self, addr):
|
||||
if addr == "":
|
||||
return None, None, None
|
||||
query = "https://api.mapbox.com/geocoding/v5/mapbox.places/" + unquote(addr) + ".json?access_token=" + self.get_public_token() + "&limit=1"
|
||||
# focus on place around last gps position
|
||||
last_pos = Params().get("LastGPSPosition")
|
||||
if last_pos is not None and last_pos != "":
|
||||
l = json.loads(last_pos)
|
||||
query += "&proximity=%s,%s" % (l["longitude"], l["latitude"])
|
||||
|
||||
r = requests.get(query)
|
||||
if r.status_code != 200:
|
||||
return None, None, None
|
||||
|
||||
j = json.loads(r.text)
|
||||
if not j["features"]:
|
||||
return None, None, None
|
||||
|
||||
feature = j["features"][0]
|
||||
return feature["place_name"], feature["center"][1], feature["center"][0]
|
||||
|
||||
def parse_POST(self):
|
||||
ctype, pdict = parse_header(self.headers['content-type'])
|
||||
if ctype == 'application/x-www-form-urlencoded':
|
||||
length = int(self.headers['content-length'])
|
||||
postvars = parse_qs(
|
||||
self.rfile.read(length).decode('utf-8'),
|
||||
keep_blank_values=1)
|
||||
else:
|
||||
postvars = {}
|
||||
return postvars
|
||||
|
||||
def gcj02towgs84(self, lng, lat):
|
||||
dlat = self.transform_lat(lng - 105.0, lat - 35.0)
|
||||
dlng = self.transform_lng(lng - 105.0, lat - 35.0)
|
||||
radlat = lat / 180.0 * pi
|
||||
magic = math.sin(radlat)
|
||||
magic = 1 - ee * magic * magic
|
||||
sqrtmagic = math.sqrt(magic)
|
||||
dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
|
||||
dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
|
||||
mglat = lat + dlat
|
||||
mglng = lng + dlng
|
||||
return [lng * 2 - mglng, lat * 2 - mglat]
|
||||
|
||||
def transform_lat(self, lng, lat):
|
||||
ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * math.sqrt(abs(lng))
|
||||
ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 * math.sin(2.0 * lng * pi)) * 2.0 / 3.0
|
||||
ret += (20.0 * math.sin(lat * pi) + 40.0 * math.sin(lat / 3.0 * pi)) * 2.0 / 3.0
|
||||
ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 * math.sin(lat * pi / 30.0)) * 2.0 / 3.0
|
||||
return ret
|
||||
|
||||
def transform_lng(self, lng, lat):
|
||||
ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * math.sqrt(abs(lng))
|
||||
ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 * math.sin(2.0 * lng * pi)) * 2.0 / 3.0
|
||||
ret += (20.0 * math.sin(lng * pi) + 40.0 * math.sin(lng / 3.0 * pi)) * 2.0 / 3.0
|
||||
ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 * math.sin(lng / 30.0 * pi)) * 2.0 / 3.0
|
||||
return ret
|
||||
|
||||
def to_json(self, lat, lng, type = "recent", name = ""):
|
||||
if name == "":
|
||||
name = str(lat) + "," + str(lng)
|
||||
new_dest = {"latitude": float(lat), "longitude": float(lng), "place_name": name}
|
||||
|
||||
if type == "recent":
|
||||
new_dest["save_type"] = "recent"
|
||||
else:
|
||||
new_dest["save_type"] = "favorite"
|
||||
new_dest["label"] = type
|
||||
|
||||
val = params.get("ApiCache_NavDestinations", encoding='utf8')
|
||||
if val is not None:
|
||||
val = val.rstrip('\x00')
|
||||
dests = [] if val is None else json.loads(val)
|
||||
|
||||
# type idx
|
||||
type_label_ids = {"home": None, "work": None, "fav1": None, "fav2": None, "fav3": None, "recent": []}
|
||||
idx = 0
|
||||
for d in dests:
|
||||
if d["save_type"] == "favorite":
|
||||
type_label_ids[d["label"]] = idx
|
||||
else:
|
||||
type_label_ids["recent"].append(idx)
|
||||
idx += 1
|
||||
|
||||
if type == "recent":
|
||||
id = None
|
||||
if len(type_label_ids["recent"]) > 10:
|
||||
dests.pop(type_label_ids["recent"][-1])
|
||||
else:
|
||||
id = type_label_ids[type]
|
||||
|
||||
if id is None:
|
||||
dests.insert(0, new_dest)
|
||||
else:
|
||||
dests[id] = new_dest
|
||||
|
||||
params.put("ApiCache_NavDestinations", json.dumps(dests).rstrip("\n\r"))
|
||||
|
||||
def main():
|
||||
webServer = HTTPServer((hostName, serverPort), OtisServ)
|
||||
|
||||
try:
|
||||
webServer.serve_forever()
|
||||
except KeyboardInterrupt:
|
||||
pass
|
||||
|
||||
webServer.server_close()
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -0,0 +1,16 @@
|
||||
<form name="searchForm" method="post">
|
||||
<fieldset class="uk-fieldset">
|
||||
<div class="uk-margin">
|
||||
<select class="uk-select" name="fav_val">
|
||||
<option value="favorites">Select Saved Destinations</option>
|
||||
<option value="home">Home</option>
|
||||
<option value="work">Work</option>
|
||||
<option value="fav1">Favorite 1</option>
|
||||
<option value="fav2">Favorite 2</option>
|
||||
<option value="fav3">Favorite 3</option>
|
||||
<div style="padding: 5px; color: red; font-weight: bold;" align="center">{{msg}}</div>
|
||||
<input class="uk-input" type="text" name="addr_val" placeholder="Search a place">
|
||||
<input class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit" value="Search">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,47 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta http-equiv="X-UA-Compatible" content="IE=edge">
|
||||
<meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
|
||||
<title>输入提示后查询</title>
|
||||
<!-- UIkit CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/css/uikit.min.css" />
|
||||
|
||||
<!-- UIkit JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit-icons.min.js"></script>
|
||||
<link rel="stylesheet" href="./style.css"/>
|
||||
<script type="text/javascript">
|
||||
window._AMapSecurityConfig = {
|
||||
securityJsCode:'{{amap_key_2}}',
|
||||
}
|
||||
</script>
|
||||
<script type="text/javascript"
|
||||
src="https://webapi.amap.com/maps?v=1.4.2&key={{amap_key}}"></script>
|
||||
<style type="text/css">
|
||||
body {
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div style="place-items: center; padding: 5px; font-weight: bold;" align="center"><a href="?reset=1"><img style="width: 100px; height: 100px; background-color: black;" src="logo.png"></a></div>
|
||||
<div class="uk-grid-match uk-grid-small uk-text-center" uk-grid>
|
||||
<div class="uk-width-1-3@m">
|
||||
<select id="save_type" class="uk-select">
|
||||
<option value="recent">最近</option>
|
||||
<option value="home">住家</option>
|
||||
<option value="work">工作</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="uk-width-expand@m">
|
||||
<input class="uk-input" type="text" id="keyword" name="keyword" placeholder="请输入关键字:(选定后搜索)" onfocus='this.value=""'/>
|
||||
</div>
|
||||
</div>
|
||||
<input type="hidden" id="longitude" />
|
||||
<input type="hidden" id="latitude" />
|
||||
<div style="height: 80%" id="container"></div>
|
||||
<script src="./index.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,120 @@
|
||||
var windowsArr = [];
|
||||
var markers = [];
|
||||
var map = new AMap.Map("container", {
|
||||
resizeEnable: true,
|
||||
center: [{{lon}}, {{lat}}], //地图中心点
|
||||
zoom: 13, //地图显示的缩放级别
|
||||
keyboardEnable: false,
|
||||
});
|
||||
|
||||
var infoWindow;
|
||||
function openInfo(name, addr, lng, lat) {
|
||||
//构建信息窗体中显示的内容
|
||||
var info = [];
|
||||
info.push('<div class="uk-card uk-card-default uk-card-body">');
|
||||
info.push('<a class="uk-card-badge uk-label" onClick="javascript:infoWindow.close()" uk-close></a>');
|
||||
info.push("<h3 style=\"padding-top: 10px;\" class=\"uk-card-title\">" + name + "</h3>");
|
||||
info.push("<p>" + addr + "</p>");
|
||||
info.push('<div class="uk-card-footer">');
|
||||
info.push('<form name="navForm" method="post">');
|
||||
info.push(' <input type="hidden" name="lat" value="' + lat + '">');
|
||||
info.push(' <input type="hidden" name="lon" value="' + lng + '">');
|
||||
info.push(' <input type="hidden" name="save_type" value="' + document.getElementById("save_type").value + '">');
|
||||
info.push(' <input type="hidden" name="name" value="' + name + '">');
|
||||
info.push(' <input class="uk-button uk-button-primary" type="submit" value="导航" >');
|
||||
info.push('</form>');
|
||||
info.push('</div>');
|
||||
info.push("</div>");
|
||||
|
||||
var pos = new AMap.LngLat(lng, lat)
|
||||
infoWindow = new AMap.InfoWindow({
|
||||
position: pos,
|
||||
isCustom: true,
|
||||
offset: new AMap.Pixel(0, -30),
|
||||
content: info.join(""), //使用默认信息窗体框样式,显示信息内容
|
||||
});
|
||||
|
||||
infoWindow.open(map, pos);
|
||||
}
|
||||
AMap.plugin(["AMap.Autocomplete", "AMap.PlaceSearch"], function () {
|
||||
var autoOptions = {
|
||||
city: "全国", //城市,默认全国
|
||||
input: "keyword", //使用联想输入的input的id
|
||||
};
|
||||
autocomplete = new AMap.Autocomplete(autoOptions);
|
||||
var placeSearch = new AMap.PlaceSearch({
|
||||
map: "",
|
||||
});
|
||||
AMap.event.addListener(autocomplete, "select", function (e) {
|
||||
//TODO 针对选中的poi实现自己的功能
|
||||
//重寫搜尋點及其提示資訊begin=====
|
||||
placeSearch.setCity(e.poi.adcode);
|
||||
if (e.poi && e.poi.location) {
|
||||
map.setZoom(17);
|
||||
map.setCenter(e.poi.location);
|
||||
}
|
||||
placeSearch.search(e.poi.name, check_dest); //關鍵字查詢查詢
|
||||
|
||||
function check_dest(status, result) {
|
||||
if (status === "complete" && result.info === "OK") {
|
||||
for (var h = 0; h < result.poiList.pois.length; h++) {
|
||||
//返回搜尋列表迴圈繫結marker
|
||||
var jy = result.poiList.pois[h]["location"]; //經緯度
|
||||
var name = result.poiList.pois[h]["name"]; //地址
|
||||
marker = new AMap.Marker({
|
||||
//加點
|
||||
map: map,
|
||||
position: jy,
|
||||
});
|
||||
marker.extData = {
|
||||
getLng: jy["lng"],
|
||||
getLat: jy["lat"],
|
||||
name: name,
|
||||
address: result.poiList.pois[h]["address"],
|
||||
}; //自定義想傳入的引數
|
||||
|
||||
marker.on("click", function (e) {
|
||||
var hs = e.target.extData;
|
||||
var content = openInfo(
|
||||
hs["name"],
|
||||
hs["address"],
|
||||
hs["getLng"],
|
||||
hs["getLat"]
|
||||
);
|
||||
});
|
||||
markers.push(marker);
|
||||
}
|
||||
}
|
||||
}
|
||||
//重寫搜尋點及其提示資訊end=====
|
||||
});
|
||||
});
|
||||
var clickEventListener = map.on('click', function(e) {
|
||||
map.remove(markers);
|
||||
document.getElementById('longitude').value = e.lnglat.getLng();
|
||||
document.getElementById('latitude').value = e.lnglat.getLat();
|
||||
lnglatXY = [e.lnglat.getLng(), e.lnglat.getLat()];
|
||||
var marker = new AMap.Marker({
|
||||
//加點
|
||||
map: map,
|
||||
position: lnglatXY,
|
||||
});
|
||||
marker.extData = {
|
||||
getLng: e.lnglat.getLng(),
|
||||
getLat: e.lnglat.getLat(),
|
||||
}; //自定義想傳入的引數
|
||||
|
||||
marker.on("click", function (e) {
|
||||
var hs = e.target.extData;
|
||||
var content = openInfo(
|
||||
"",
|
||||
"(" + hs["getLat"] + ", " + hs["getLng"] + ")",
|
||||
hs["getLng"],
|
||||
hs["getLat"]
|
||||
);
|
||||
});
|
||||
markers.push(marker);
|
||||
if (typeof(infoWindow) != "undefined") {
|
||||
infoWindow.close();
|
||||
}
|
||||
});
|
||||
@@ -0,0 +1,11 @@
|
||||
<form name="setAmapTokenForm" method="post">
|
||||
<fieldset class="uk-fieldset">
|
||||
<legend class="uk-legend">请输入您的高德地图 API KEY</legend>
|
||||
<div style="color: red">因系统升级,若于 2021/12/02 前申请 key 的人请重新申请新的「<b>key</b>」和「<b>安全密钥</b>」配对。</div>
|
||||
<div class="uk-margin">
|
||||
<input class="uk-input" type="text" name="amap_key_val" placeholder="KEY">
|
||||
<input class="uk-input" type="text" name="amap_key_val_2" placeholder="安全密钥">
|
||||
<input class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit" value="设置">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,40 @@
|
||||
html, body {
|
||||
margin: 0;
|
||||
height: 100%;
|
||||
width: 100%;
|
||||
position: absolute;
|
||||
}
|
||||
|
||||
#mapContainer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.button-group {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
right: 20px;
|
||||
font-size: 12px;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.button-group .button {
|
||||
height: 28px;
|
||||
line-height: 28px;
|
||||
background-color: #0D9BF2;
|
||||
color: #FFF;
|
||||
border: 0;
|
||||
outline: none;
|
||||
padding-left: 5px;
|
||||
padding-right: 5px;
|
||||
border-radius: 3px;
|
||||
margin-bottom: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.amap-info-content {
|
||||
font-size: 12px;
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
<form name="setSkTokenForm" method="post">
|
||||
<fieldset class="uk-fieldset">
|
||||
<legend class="uk-legend">Set your Mapbox <b>APP TOKEN</b></legend>
|
||||
<div style="padding: 5px; color: red; font-weight: bold;">{{msg}}</div>
|
||||
<div class="uk-margin">
|
||||
<input class="uk-input" type="text" name="sk_token_val" placeholder="e.g.: sk.xxxxxxx...">
|
||||
<input class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit" value="Set">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,20 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>sunnypilot</title>
|
||||
<meta name="viewport" content="initial-scale=1,maximum-scale=1,user-scalable=no">
|
||||
<!-- UIkit CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/css/uikit.min.css" />
|
||||
|
||||
<!-- UIkit JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit-icons.min.js"></script>
|
||||
</head>
|
||||
<body style="margin: 0; padding: 0;">
|
||||
<div style="display: grid; place-items: center;">
|
||||
<div style="padding: 5px; font-weight: bold;" align="center"><a href="?reset=1"><img style="width: 100px; height: 100px; background-color: black;" src="logo.png"></a></div>
|
||||
{{content}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,39 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>sunnypilot</title>
|
||||
<script src="https://polyfill.io/v3/polyfill.min.js?features=default"></script>
|
||||
<link rel="stylesheet" type="text/css" href="./style.css" />
|
||||
<!-- UIkit CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/css/uikit.min.css" />
|
||||
|
||||
<!-- UIkit JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/uikit@3.9.2/dist/js/uikit-icons.min.js"></script>
|
||||
|
||||
<script src="./index.js"></script>
|
||||
<meta name="viewport" content="width=device-width">
|
||||
</head>
|
||||
<body>
|
||||
<div style="place-items: center; padding: 5px; font-weight: bold;" align="center"><a href="?reset=1"><img style="width: 100px; height: 100px; background-color: black;" src="logo.png"></a></div>
|
||||
<div class="uk-grid-match uk-grid-small uk-text-center" uk-grid>
|
||||
<div class="uk-width-1-3@m">
|
||||
<select id="save_type" class="uk-select" name="type">
|
||||
<option value="recent">Recent</option>
|
||||
<option value="home">Home</option>
|
||||
<option value="work">Work</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="uk-width-expand@m">
|
||||
<input class="uk-input" type="text" id="pac-input" name="keyword" placeholder="Search a place" onfocus='this.value=""'/>
|
||||
</div>
|
||||
</div>
|
||||
<div id="map"></div>
|
||||
<!-- Async script executes immediately and must be after any DOM elements used in callback. -->
|
||||
<script
|
||||
src="https://maps.googleapis.com/maps/api/js?key={{gmap_key}}&callback=initAutocomplete&libraries=places&v=weekly"
|
||||
async
|
||||
></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,86 @@
|
||||
// This example adds a search box to a map, using the Google Place Autocomplete
|
||||
// feature. People can enter geographical searches. The search box will return a
|
||||
// pick list containing a mix of places and predicted search terms.
|
||||
// This example requires the Places library. Include the libraries=places
|
||||
// parameter when you first load the API. For example:
|
||||
// <script src="https://maps.googleapis.com/maps/api/js?key=YOUR_API_KEY&libraries=places">
|
||||
function initAutocomplete() {
|
||||
const map = new google.maps.Map(document.getElementById("map"), {
|
||||
center: { lat: {{lat}}, lng: {{lon}} },
|
||||
zoom: 13,
|
||||
mapTypeId: "roadmap",
|
||||
disableDefaultUI: true
|
||||
});
|
||||
// Create the search box and link it to the UI element.
|
||||
const input = document.getElementById("pac-input");
|
||||
const searchBox = new google.maps.places.SearchBox(input);
|
||||
|
||||
// Bias the SearchBox results towards current map's viewport.
|
||||
map.addListener("bounds_changed", () => {
|
||||
searchBox.setBounds(map.getBounds());
|
||||
});
|
||||
|
||||
let markers = [];
|
||||
|
||||
// Listen for the event fired when the user selects a prediction and retrieve
|
||||
// more details for that place.
|
||||
searchBox.addListener("places_changed", () => {
|
||||
const places = searchBox.getPlaces();
|
||||
|
||||
if (places.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Clear out the old markers.
|
||||
markers.forEach((marker) => {
|
||||
marker.setMap(null);
|
||||
});
|
||||
markers = [];
|
||||
|
||||
// For each place, get the icon, name and location.
|
||||
const bounds = new google.maps.LatLngBounds();
|
||||
|
||||
places.slice(-1).forEach((place) => {
|
||||
if (!place.geometry || !place.geometry.location) {
|
||||
console.log("Returned place contains no geometry");
|
||||
return;
|
||||
}
|
||||
|
||||
const icon = {
|
||||
url: place.icon,
|
||||
size: new google.maps.Size(71, 71),
|
||||
origin: new google.maps.Point(0, 0),
|
||||
anchor: new google.maps.Point(17, 34),
|
||||
scaledSize: new google.maps.Size(25, 25),
|
||||
};
|
||||
|
||||
// Create a marker for each place.
|
||||
markers.push(
|
||||
new google.maps.Marker({
|
||||
map,
|
||||
icon,
|
||||
title: place.name,
|
||||
position: place.geometry.location,
|
||||
})
|
||||
);
|
||||
|
||||
// set nav
|
||||
var http = new XMLHttpRequest();
|
||||
http.open("POST", "/", true);
|
||||
http.setRequestHeader("Content-type","application/x-www-form-urlencoded");
|
||||
var params = "lat=" + place.geometry.location.lat() + "&lon=" + place.geometry.location.lng();
|
||||
params += "&save_type=" + document.getElementById("save_type").value;
|
||||
params += "&name=" + place.name;
|
||||
http.send(params);
|
||||
|
||||
if (place.geometry.viewport) {
|
||||
// Only geocodes have viewport.
|
||||
bounds.union(place.geometry.viewport);
|
||||
} else {
|
||||
bounds.extend(place.geometry.location);
|
||||
}
|
||||
return;
|
||||
});
|
||||
map.fitBounds(bounds);
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
<form name="setGmapTokenForm" method="post">
|
||||
<fieldset class="uk-fieldset">
|
||||
<legend class="uk-legend">Set your Google Map API Key</legend>
|
||||
<div class="uk-margin">
|
||||
<input class="uk-input" type="text" name="gmap_key_val" placeholder="Google Map API KEY">
|
||||
<input class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit" value="设置">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,72 @@
|
||||
/* Always set the map height explicitly to define the size of the div
|
||||
* element that contains the map. */
|
||||
#map {
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
/* Optional: Makes the sample page fill the window. */
|
||||
html,
|
||||
body {
|
||||
height: 80%;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#description {
|
||||
font-family: Roboto;
|
||||
font-size: 15px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
#infowindow-content .title {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
#infowindow-content {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#map #infowindow-content {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.pac-card {
|
||||
background-color: #fff;
|
||||
border: 0;
|
||||
border-radius: 2px;
|
||||
box-shadow: 0 1px 4px -1px rgba(0, 0, 0, 0.3);
|
||||
margin: 10px;
|
||||
padding: 0 0.5em;
|
||||
font: 400 18px Roboto, Arial, sans-serif;
|
||||
overflow: hidden;
|
||||
font-family: Roboto;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
#pac-container {
|
||||
padding-bottom: 12px;
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.pac-controls {
|
||||
display: inline-block;
|
||||
padding: 5px 11px;
|
||||
}
|
||||
|
||||
.pac-controls label {
|
||||
font-family: Roboto;
|
||||
font-size: 13px;
|
||||
font-weight: 300;
|
||||
}
|
||||
|
||||
#title {
|
||||
color: #fff;
|
||||
background-color: #4d90fe;
|
||||
font-size: 25px;
|
||||
font-weight: 500;
|
||||
padding: 6px 12px;
|
||||
}
|
||||
|
||||
#target {
|
||||
width: 345px;
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
<div><img src="https://api.mapbox.com/styles/v1/mapbox/streets-v11/static/pin-s-l+000({{lon}},{{lat}})/{{lon}},{{lat}},14/300x300?access_token={{token}}" /></div>
|
||||
<div style="padding: 5px; font-size: 10px;">{{addr}}</div>
|
||||
<form name="navForm" method="post">
|
||||
<fieldset class="uk-fieldset">
|
||||
<div class="uk-margin">
|
||||
<input type="hidden" name="name" value="{{addr}}">
|
||||
<input type="hidden" name="lat" value="{{lat}}">
|
||||
<input type="hidden" name="lon" value="{{lon}}">
|
||||
<select id="save_type" name="save_type" class="uk-select">
|
||||
<option value="recent">Recent</option>
|
||||
<option value="home">Set Home</option>
|
||||
<option value="work">Set Work</option>
|
||||
<option value="fav1">Set Favorite 1</option>
|
||||
<option value="fav2">Set Favorite 2</option>
|
||||
<option value="fav3">Set Favorite 3</option>
|
||||
</select>
|
||||
<input class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit" value="Start Navigation">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
@@ -0,0 +1,10 @@
|
||||
<form name="setPkTokenForm" method="post">
|
||||
<fieldset class="uk-fieldset">
|
||||
<legend class="uk-legend">Set your Mapbox <b>PUBLIC TOKEN</b></legend>
|
||||
<div style="padding: 5px; color: red; font-weight: bold;">{{msg}}</div>
|
||||
<div class="uk-margin">
|
||||
<input class="uk-input" type="text" name="pk_token_val" placeholder="e.g.: pk.xxxxxxx...">
|
||||
<input class="uk-button uk-button-primary uk-width-1-1 uk-margin-small-bottom" type="submit" value="Set">
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
Reference in New Issue
Block a user