Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
G
gk
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
micro_app
gk
Commits
c6703378
Commit
c6703378
authored
Oct 29, 2026
by
daniel
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'feature/location-map-enhance' into master
parents
5c1d9b52
af7e0b18
Show whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
390 additions
and
125 deletions
+390
-125
locationPoint.jsp
src/main/webapp/WEB-INF/views/cmpt/locationPoint.jsp
+390
-125
No files found.
src/main/webapp/WEB-INF/views/cmpt/locationPoint.jsp
View file @
c6703378
...
@@ -31,12 +31,6 @@
...
@@ -31,12 +31,6 @@
<link
href=
"https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel=
"stylesheet"
>
<link
href=
"https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.css"
rel=
"stylesheet"
>
<!-- 添加Mapbox GL JS -->
<!-- 添加Mapbox GL JS -->
<script
src=
"https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"
></script>
<script
src=
"https://api.mapbox.com/mapbox-gl-js/v3.3.0/mapbox-gl.js"
></script>
<!-- 添加Mapbox GL Geocoder JS -->
<script
src=
"https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.1-dev/mapbox-gl-geocoder.min.js"
></script>
<link
rel=
"stylesheet"
href=
"https://api.mapbox.com/mapbox-gl-js/plugins/mapbox-gl-geocoder/v5.0.1-dev/mapbox-gl-geocoder.css"
type=
"text/css"
/>
<!-- Favicon-->
<!-- Favicon-->
<link
rel=
"shortcut icon"
href=
"https://img.geexek.com/files/home/favicon.ico"
>
<link
rel=
"shortcut icon"
href=
"https://img.geexek.com/files/home/favicon.ico"
>
<!-- 自定义css -->
<!-- 自定义css -->
...
@@ -69,10 +63,36 @@
...
@@ -69,10 +63,36 @@
display
:
none
;
display
:
none
;
}
}
/* 下移 50px,右移 10px(可根据需要调整) */
.search-container
{
.mapboxgl-ctrl-top-left
.mapboxgl-ctrl-geocoder
{
position
:
absolute
;
margin-top
:
90px
;
top
:
0
;
/* margin-left: 10px; */
left
:
0
;
z-index
:
10
;
width
:
300px
;
}
.search-results
{
background
:
white
;
border-radius
:
4px
;
margin-top
:
4px
;
max-height
:
300px
;
overflow-y
:
auto
;
box-shadow
:
0
2px
8px
rgba
(
0
,
0
,
0
,
0.15
);
}
.search-result-item
{
padding
:
8px
12px
;
cursor
:
pointer
;
border-bottom
:
1px
solid
#f0f0f0
;
font-size
:
13px
;
}
.search-result-item
:hover
{
background
:
#f5f5f5
;
}
.hover-underline
:hover
{
text-decoration
:
underline
;
}
}
</style>
</style>
</head>
</head>
...
@@ -95,37 +115,44 @@
...
@@ -95,37 +115,44 @@
<
div
style
=
"position: fixed; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding: 0;"
>
<
div
style
=
"position: fixed; top: 0; left: 0; right: 0; bottom: 0; margin: 0; padding: 0;"
>
<
div
id
=
"map"
style
=
"width: 100%; height: 100%;z-index: 0;"
><
/div
>
<
div
id
=
"map"
style
=
"width: 100%; height: 100%;z-index: 0;"
><
/div
>
<
div
class
=
"fixed left-0 top-0 z-10 flex flex-col justify-center items-start gap-y-2 p-3"
>
<
div
class
=
"fixed left-0 top-0 z-10 flex flex-col justify-center items-start gap-y-2 p-3"
>
<!--
<
input
type
=
"file"
accept
=
".gpx,.kml"
@
change
=
"handleFileChange"
style
=
"display: none"
ref
=
"fileInputRef"
/>
<
n
-
card
>
<
div
class
=
"flex flex-col items-start gap-y-2"
>
<
n
-
button
@
click
=
"fileInputRef?.click()"
type
=
"primary"
>
导入轨迹
<
/n-button
>
<
span
class
=
"text-gray text-sm"
>
只能上传
GPX
或
KML
文件,且不超过
10
M
。
<
/span
>
<
span
class
=
"text-gray text-blue-600"
v
-
if
=
"fileName"
>
{{
fileName
}}
<
/span
>
<
/div
>
<
/n-card> --
>
<
n
-
button
@
click
=
"onBack"
type
=
"primary"
>
<
n
-
button
@
click
=
"onBack"
type
=
"primary"
>
返回
返回
<
/n-button
>
<
/n-button
>
<
n
-
switch
v
-
model
:
value
=
"isSatelliteLayer"
@
update
:
value
=
"toggleMapLayer"
>
<
n
-
card
size
=
"small"
style
=
"min-width:200px;"
>
<
template
#
checked
>
<
div
class
=
"flex flex-col gap-y-2"
>
卫星地图
<
span
style
=
"font-size:13px;font-weight:500;"
>
底图
<
/span
>
<
/template
>
<
n
-
radio
-
group
v
-
model
:
value
=
"mapLayer"
@
update
:
value
=
"applyMapStyle"
size
=
"small"
>
<
template
#
unchecked
>
<
n
-
radio
-
button
v
-
for
=
"item in MapLayerEnum"
:
key
=
"item.value"
:
value
=
"item.value"
>
{{
item
.
label
}}
<
/n-radio-button
>
普通地图
<
/n-radio-group
>
<
/template
>
<
n
-
radio
-
group
v
-
model
:
value
=
"mapType"
@
update
:
value
=
"applyMapStyle"
size
=
"small"
>
<
/n-switch
>
<
n
-
radio
-
button
v
-
for
=
"item in MapTypeEnum"
:
key
=
"item.value"
:
value
=
"item.value"
>
{{
item
.
label
}}
<
/n-radio-button
>
<
/n-radio-group
>
<
/div
>
<
/n-card
>
<
div
class
=
"search-container"
style
=
"position:relative;"
>
<
n
-
input
-
group
>
<
n
-
input
v
-
model
:
value
=
"searchKeyword"
placeholder
=
"输入地点搜索..."
clearable
@
input
=
"onSearchInput"
@
clear
=
"searchResults = []; showSearchResults = false;"
@
keyup
.
enter
=
"doSearch"
/>
<
n
-
button
type
=
"primary"
:
loading
=
"searchLoading"
@
click
=
"doSearch"
>
搜索
<
/n-button
>
<
/n-input-group
>
<
div
v
-
if
=
"showSearchResults && searchResults.length"
class
=
"search-results"
>
<
div
v
-
for
=
"(item, index) in searchResults"
:
key
=
"index"
@
click
=
"onSelectSearchResult(item)"
class
=
"search-result-item"
>
<
div
style
=
"font-weight:500;"
>
{{
item
.
name
}}
<
/div
>
<
div
style
=
"color:#999;font-size:12px;"
>
{{
item
.
address
}}
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
/div
>
<
div
class
=
"fixed right-3 top-0 z-10 p-3 flex flex-col gap-y-1"
>
<
div
class
=
"fixed right-3 top-0 z-10 p-3 flex flex-col gap-y-1"
>
<
div
class
=
"flex flex-row items-center mb-2 w-full gap-x-1 bg-white p-2 rounded-sm"
>
<
div
class
=
"flex flex-row items-center mb-2 w-full gap-x-1 bg-white p-2 rounded-sm"
>
...
@@ -147,8 +174,8 @@
...
@@ -147,8 +174,8 @@
:
key
=
"marker.id"
:
key
=
"marker.id"
class
=
"flex flex-row items-center gap-x-2 py-1"
class
=
"flex flex-row items-center gap-x-2 py-1"
>
>
<
span
class
=
"flex flex-1 flex-row items-center gap-x-1 text-xs"
>
<
span
class
=
"flex flex-1 flex-row items-center gap-x-1 text-xs
cursor-pointer"
@
click
=
"flyToMarker(marker)
"
>
<
span
class
=
"line-clamp-1 flex-1 font-bold text-md"
>
<
span
class
=
"line-clamp-1 flex-1 font-bold text-md
hover-underline
"
>
{{
marker
.
name
}}
{{
marker
.
name
}}
<
/span
>
<
/span
>
<
n
-
tag
size
=
"small"
:
bordered
=
"false"
:
type
=
"StatusEnum[marker.status].color"
>
<
n
-
tag
size
=
"small"
:
bordered
=
"false"
:
type
=
"StatusEnum[marker.status].color"
>
...
@@ -244,7 +271,7 @@
...
@@ -244,7 +271,7 @@
<script>
<script>
const
cmpt
=
JSON
.
parse
(
'${fns:toJson(cmpt)}'
);
const
cmpt
=
JSON
.
parse
(
'${fns:toJson(cmpt)}'
);
const
tk
=
"1
6a595e56ea0cec57ad8c07368d7f1ac
"
const
tk
=
"1
599ebcef853d2efc608e4ef66b46bb3
"
// 地图用到的图标
// 地图用到的图标
const
mapIcons
=
{
const
mapIcons
=
{
0
:
'https://geexekimgoss-sxk.oss-cn-hangzhou.aliyuncs.com/gk/common/map-icon/icon-cp.png'
,
0
:
'https://geexekimgoss-sxk.oss-cn-hangzhou.aliyuncs.com/gk/common/map-icon/icon-cp.png'
,
...
@@ -374,29 +401,185 @@
...
@@ -374,29 +401,185 @@
},
},
};
};
// 底图源枚举
const
MapLayerEnum
=
Object
.
freeze
({
TIANDITU
:
{
value
:
'tianditu'
,
label
:
'天地图'
,
gcj02
:
false
},
GAODE
:
{
value
:
'gaode'
,
label
:
'高德'
,
gcj02
:
true
},
TENCENT
:
{
value
:
'tencent'
,
label
:
'腾讯'
,
gcj02
:
true
},
});
// 底图类型枚举
const
MapTypeEnum
=
Object
.
freeze
({
SATELLITE
:
{
value
:
'satellite'
,
label
:
'卫星'
},
NORMAL
:
{
value
:
'normal'
,
label
:
'普通'
},
});
// 获取天地图的地址
// 获取天地图的地址
const
getTDTUrls
=
(
type
=
'satellite'
)
=>
{
const
getTDTUrls
=
(
type
=
MapTypeEnum
.
SATELLITE
.
value
)
=>
{
const
services
=
Array
.
from
({
length
:
7
},
(
_
,
i
)
=>
i
+
1
);
const
services
=
Array
.
from
({
length
:
7
},
(
_
,
i
)
=>
i
+
1
);
const
baseUrl
=
'https://t%s.tianditu.gov.cn'
;
const
baseUrl
=
'https://t%s.tianditu.gov.cn'
;
const
generateUrls
=
(
path
)
=>
{
const
generateUrls
=
(
path
)
=>
{
return
services
.
map
((
v
)
=>
baseUrl
.
replace
(
'%s'
,
v
.
toString
())
+
path
);
return
services
.
map
((
v
)
=>
baseUrl
.
replace
(
'%s'
,
v
.
toString
())
+
path
);
};
};
// ===== 普通矢量图层(非卫星) =====
const
normalPath
=
"/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
const
normalPath
=
"/vec_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=vec&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
const
TDTNormalUrls
=
generateUrls
(
normalPath
);
const
TDTNormalUrls
=
generateUrls
(
normalPath
);
// ===== 卫星图层 =====
const
satellitePath
=
"/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
const
satellitePath
=
"/img_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=img&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
const
TDTSatelliteUrls
=
generateUrls
(
satellitePath
);
const
TDTSatelliteUrls
=
generateUrls
(
satellitePath
);
const
satelliteAnnotationPath
=
"/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cia&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
// ===== 注记层(通用) =====
const
TDTSatelliteAnnotationUrls
=
generateUrls
(
satelliteAnnotationPath
);
const
annotationPath
=
"/cia_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ci
a&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
const
normalAnnotationPath
=
"/cva_w/wmts?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=cv
a&STYLE=default&TILEMATRIXSET=w&FORMAT=tiles&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&tk="
+
tk
;
const
TDT
AnnotationUrls
=
generateUrls
(
a
nnotationPath
);
const
TDT
NormalAnnotationUrls
=
generateUrls
(
normalA
nnotationPath
);
return
{
return
{
TDTNormalUrls
,
TDTNormalUrls
,
TDTSatelliteUrls
,
TDTSatelliteUrls
,
TDTAnnotationUrls
,
TDTSatelliteAnnotationUrls
,
TDTNormalAnnotationUrls
,
};
}
// 获取高德地图的瓦片地址
const
getGaodeUrls
=
()
=>
{
const
services
=
[
1
,
2
,
3
,
4
];
return
{
GaodeSatelliteUrls
:
services
.
map
(
v
=>
'https://webst0'
+
v
+
'.is.autonavi.com/appmaptile?style=6&x={x}&y={y}&z={z}'
),
GaodeNormalUrls
:
services
.
map
(
v
=>
'https://webrd0'
+
v
+
'.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=7&x={x}&y={y}&z={z}'
),
GaodeAnnotationUrls
:
services
.
map
(
v
=>
'https://webst0'
+
v
+
'.is.autonavi.com/appmaptile?style=8&x={x}&y={y}&z={z}'
),
};
}
// 腾讯卫星瓦片占位 host
const
TENCENT_SATELLITE_HOST
=
'tencent-satellite.invalid'
;
// 获取腾讯地图的瓦片地址
const
getTencentUrls
=
()
=>
{
const
services
=
[
0
,
1
,
2
,
3
];
return
{
TencentSatelliteUrls
:
[
'https://'
+
TENCENT_SATELLITE_HOST
+
'/tiles/{z}/{x}/{y}'
],
TencentNormalUrls
:
services
.
map
(
v
=>
'https://rt'
+
v
+
'.map.gtimg.com/tile?z={z}&x={x}&y={y}&styleid=0&scene=0&version=2213'
),
TencentAnnotationUrls
:
services
.
map
(
v
=>
'https://rt'
+
v
+
'.map.gtimg.com/tile?z={z}&x={x}&y={y}&styleid=2&scene=0&version=2213'
),
};
}
// 腾讯卫星瓦片 URL 转换(TMS Y + 分桶目录)
const
transformTencentSatelliteUrl
=
(
url
)
=>
{
let
parsedUrl
;
try
{
parsedUrl
=
new
URL
(
url
);
}
catch
{
return
url
;
}
if
(
parsedUrl
.
hostname
!==
TENCENT_SATELLITE_HOST
)
return
url
;
const
match
=
parsedUrl
.
pathname
.
match
(
/^
\/
tiles
\/(\d
+
)\/(\d
+
)\/(\d
+
)
$/
);
if
(
!
match
)
return
url
;
const
[,
zText
,
xText
,
xyzYText
]
=
match
;
const
z
=
Number
(
zText
);
const
x
=
Number
(
xText
);
const
y
=
Math
.
pow
(
2
,
z
)
-
1
-
Number
(
xyzYText
);
const
server
=
(
x
+
y
)
%
4
;
return
'https://p'
+
server
+
'.map.gtimg.com/sateTiles/'
+
z
+
'/'
+
Math
.
floor
(
x
/
16
)
+
'/'
+
Math
.
floor
(
y
/
16
)
+
'/'
+
x
+
'_'
+
y
+
'.jpg?version=276'
;
}
// ===== 坐标转换(WGS-84 ⇔ GCJ-02)=====
function
outOfChina
(
lng
,
lat
)
{
return
lng
<
72.004
||
lng
>
137.8347
||
lat
<
0.8293
||
lat
>
55.8271
;
}
function
_transformLat
(
x
,
y
)
{
let
ret
=
-
100
+
2
*
x
+
3
*
y
+
0.2
*
y
*
y
+
0.1
*
x
*
y
+
0.2
*
Math
.
sqrt
(
Math
.
abs
(
x
));
ret
+=
((
20
*
Math
.
sin
(
6
*
x
*
Math
.
PI
)
+
20
*
Math
.
sin
(
2
*
x
*
Math
.
PI
))
*
2
)
/
3
;
ret
+=
((
20
*
Math
.
sin
(
y
*
Math
.
PI
)
+
40
*
Math
.
sin
((
y
/
3
)
*
Math
.
PI
))
*
2
)
/
3
;
ret
+=
((
160
*
Math
.
sin
((
y
/
12
)
*
Math
.
PI
)
+
320
*
Math
.
sin
((
y
*
Math
.
PI
)
/
30
))
*
2
)
/
3
;
return
ret
;
}
function
_transformLng
(
x
,
y
)
{
let
ret
=
300
+
x
+
2
*
y
+
0.1
*
x
*
x
+
0.1
*
x
*
y
+
0.1
*
Math
.
sqrt
(
Math
.
abs
(
x
));
ret
+=
((
20
*
Math
.
sin
(
6
*
x
*
Math
.
PI
)
+
20
*
Math
.
sin
(
2
*
x
*
Math
.
PI
))
*
2
)
/
3
;
ret
+=
((
20
*
Math
.
sin
(
x
*
Math
.
PI
)
+
40
*
Math
.
sin
((
x
/
3
)
*
Math
.
PI
))
*
2
)
/
3
;
ret
+=
((
150
*
Math
.
sin
((
x
/
12
)
*
Math
.
PI
)
+
300
*
Math
.
sin
((
x
/
30
)
*
Math
.
PI
))
*
2
)
/
3
;
return
ret
;
}
const
EE
=
0.00669342162296594323
;
const
A
=
6378245.0
;
function
wgs84ToGcj02
(
lng
,
lat
)
{
if
(
outOfChina
(
lng
,
lat
))
return
[
lng
,
lat
];
let
dLat
=
_transformLat
(
lng
-
105
,
lat
-
35
);
let
dLng
=
_transformLng
(
lng
-
105
,
lat
-
35
);
const
radLat
=
(
lat
/
180
)
*
Math
.
PI
;
let
magic
=
Math
.
sin
(
radLat
);
magic
=
1
-
EE
*
magic
*
magic
;
const
sqrtMagic
=
Math
.
sqrt
(
magic
);
dLat
=
(
dLat
*
180
)
/
(((
A
*
(
1
-
EE
))
/
(
magic
*
sqrtMagic
))
*
Math
.
PI
);
dLng
=
(
dLng
*
180
)
/
((
A
/
sqrtMagic
)
*
Math
.
cos
(
radLat
)
*
Math
.
PI
);
return
[
lng
+
dLng
,
lat
+
dLat
];
}
function
gcj02ToWgs84
(
lng
,
lat
)
{
if
(
outOfChina
(
lng
,
lat
))
return
[
lng
,
lat
];
let
dLat
=
_transformLat
(
lng
-
105
,
lat
-
35
);
let
dLng
=
_transformLng
(
lng
-
105
,
lat
-
35
);
const
radLat
=
(
lat
/
180
)
*
Math
.
PI
;
let
magic
=
Math
.
sin
(
radLat
);
magic
=
1
-
EE
*
magic
*
magic
;
const
sqrtMagic
=
Math
.
sqrt
(
magic
);
dLat
=
(
dLat
*
180
)
/
(((
A
*
(
1
-
EE
))
/
(
magic
*
sqrtMagic
))
*
Math
.
PI
);
dLng
=
(
dLng
*
180
)
/
((
A
/
sqrtMagic
)
*
Math
.
cos
(
radLat
)
*
Math
.
PI
);
const
mgLat
=
lat
+
dLat
;
const
mgLng
=
lng
+
dLng
;
return
[
lng
*
2
-
mgLng
,
lat
*
2
-
mgLat
];
}
// 判断底图是否使用 GCJ-02 坐标系
function
usesGcj02
(
layer
)
{
const
entry
=
Object
.
values
(
MapLayerEnum
).
find
(
e
=>
e
.
value
===
layer
);
return
entry
?
entry
.
gcj02
:
false
;
}
// 生成 Mapbox style 对象
const
getMapStyle
=
(
layer
,
type
)
=>
{
const
isSatellite
=
type
===
MapTypeEnum
.
SATELLITE
.
value
;
const
{
TDTNormalUrls
,
TDTSatelliteUrls
,
TDTSatelliteAnnotationUrls
,
TDTNormalAnnotationUrls
}
=
getTDTUrls
();
const
{
GaodeSatelliteUrls
,
GaodeNormalUrls
,
GaodeAnnotationUrls
}
=
getGaodeUrls
();
const
{
TencentSatelliteUrls
,
TencentNormalUrls
,
TencentAnnotationUrls
}
=
getTencentUrls
();
const
tileSets
=
{
[
MapLayerEnum
.
TIANDITU
.
value
]:
{
base
:
isSatellite
?
TDTSatelliteUrls
:
TDTNormalUrls
,
annotation
:
isSatellite
?
TDTSatelliteAnnotationUrls
:
TDTNormalAnnotationUrls
,
},
[
MapLayerEnum
.
GAODE
.
value
]:
{
base
:
isSatellite
?
GaodeSatelliteUrls
:
GaodeNormalUrls
,
annotation
:
isSatellite
?
GaodeAnnotationUrls
:
null
,
},
[
MapLayerEnum
.
TENCENT
.
value
]:
{
base
:
isSatellite
?
TencentSatelliteUrls
:
TencentNormalUrls
,
baseScheme
:
isSatellite
?
'xyz'
:
'tms'
,
annotation
:
isSatellite
?
TencentAnnotationUrls
:
null
,
annotationScheme
:
isSatellite
?
'tms'
:
undefined
,
},
};
const
selected
=
tileSets
[
layer
]
||
tileSets
[
MapLayerEnum
.
TIANDITU
.
value
];
const
sources
=
{
basemap
:
{
type
:
'raster'
,
tiles
:
selected
.
base
,
tileSize
:
256
,
scheme
:
selected
.
baseScheme
||
'xyz'
,
},
};
};
const
layers
=
[
{
id
:
'basemap-layer'
,
type
:
'raster'
,
source
:
'basemap'
},
];
if
(
selected
.
annotation
)
{
sources
.
annotation
=
{
type
:
'raster'
,
tiles
:
selected
.
annotation
,
tileSize
:
256
,
scheme
:
selected
.
annotationScheme
||
'xyz'
,
};
layers
.
push
({
id
:
'annotation-layer'
,
type
:
'raster'
,
source
:
'annotation'
});
}
return
{
version
:
8
,
sources
,
layers
,
glyphs
};
}
}
// mapbox的字体
// mapbox的字体
const
glyphs
=
'mapbox://fonts/mapbox/{fontstack}/{range}.pbf'
;
const
glyphs
=
'mapbox://fonts/mapbox/{fontstack}/{range}.pbf'
;
...
@@ -425,48 +608,28 @@
...
@@ -425,48 +608,28 @@
const
message
=
useMessage
()
const
message
=
useMessage
()
const
notification
=
useNotification
()
const
notification
=
useNotification
()
// 是否是卫星地图图层
// 底图状态
const
isSatelliteLayer
=
ref
(
true
)
const
mapLayer
=
ref
(
MapLayerEnum
.
TIANDITU
.
value
)
// 获取地址
const
mapType
=
ref
(
MapTypeEnum
.
SATELLITE
.
value
)
const
{
TDTNormalUrls
,
TDTSatelliteUrls
,
TDTAnnotationUrls
}
=
getTDTUrls
();
// 构建样式
// 坐标转换辅助
const
mapStyle
=
computed
(()
=>
({
const
shouldTransformCoord
=
computed
(()
=>
usesGcj02
(
mapLayer
.
value
))
version
:
8
,
const
transformCoord
=
(
lng
,
lat
)
=>
{
sources
:
{
if
(
!
shouldTransformCoord
.
value
)
return
[
lng
,
lat
];
tianmap
:
{
return
wgs84ToGcj02
(
lng
,
lat
);
type
:
'raster'
,
}
tiles
:
isSatelliteLayer
.
value
?
TDTSatelliteUrls
:
TDTNormalUrls
,
const
reverseTransformCoord
=
(
lng
,
lat
)
=>
{
tileSize
:
256
,
if
(
!
shouldTransformCoord
.
value
)
return
[
lng
,
lat
];
attribution
:
isSatelliteLayer
.
value
?
'天地图卫星影像图'
:
'天地图矢量图'
,
return
gcj02ToWgs84
(
lng
,
lat
);
},
}
tianmapAnnotation
:
{
type
:
'raster'
,
// 搜索相关
tiles
:
TDTAnnotationUrls
,
const
searchKeyword
=
ref
(
''
)
tileSize
:
256
,
const
searchResults
=
ref
([])
attribution
:
'天地图注记图层'
,
const
searchLoading
=
ref
(
false
)
},
const
showSearchResults
=
ref
(
false
)
},
let
searchTimer
=
null
layers
:
[
let
lastSearchTime
=
0
{
id
:
'tianmap-layer'
,
type
:
'raster'
,
source
:
'tianmap'
,
},
{
id
:
'tianmap-annotation-layer'
,
type
:
'raster'
,
source
:
'tianmapAnnotation'
,
},
],
glyphs
,
}));
// 添加搜索输入框
const
geocoder
=
new
MapboxGeocoder
({
accessToken
:
mapBoxToken
,
mapboxgl
:
mapboxgl
,
placeholder
:
'输入地点搜索...'
,
marker
:
false
// 不自动加 marker,可自行控制
});
onMounted
(()
=>
{
onMounted
(()
=>
{
getCurrentPosition
()
getCurrentPosition
()
...
@@ -522,11 +685,6 @@
...
@@ -522,11 +685,6 @@
/**------------------------ 地图初始化 start ------------------------**/
/**------------------------ 地图初始化 start ------------------------**/
// 地图对象
// 地图对象
let
map
=
ref
(
null
);
let
map
=
ref
(
null
);
// 监听选中事件
geocoder
.
on
(
'result'
,
(
e
)
=>
{
const
{
center
,
place_name
}
=
e
.
result
;
console
.
log
(
'选中地点:'
,
place_name
,
center
);
});
// 初始化地图
// 初始化地图
const
initMap
=
()
=>
{
const
initMap
=
()
=>
{
// 确保容器已完全渲染
// 确保容器已完全渲染
...
@@ -535,26 +693,29 @@
...
@@ -535,26 +693,29 @@
if
(
!
map
.
value
)
{
if
(
!
map
.
value
)
{
map
.
value
=
new
mapboxgl
.
Map
({
map
.
value
=
new
mapboxgl
.
Map
({
container
:
'map'
,
container
:
'map'
,
style
:
mapStyle
.
value
,
style
:
getMapStyle
(
mapLayer
.
value
,
mapType
.
value
),
container
:
'map'
,
center
:
transformCoord
(
GpsConfig
.
map
.
center
[
0
],
GpsConfig
.
map
.
center
[
1
]),
center
:
GpsConfig
.
map
.
center
,
zoom
:
GpsConfig
.
map
.
zoom
,
zoom
:
GpsConfig
.
map
.
zoom
,
accessToken
:
mapBoxToken
,
accessToken
:
mapBoxToken
,
minZoom
:
GpsConfig
.
map
.
minZoom
,
minZoom
:
GpsConfig
.
map
.
minZoom
,
maxZoom
:
GpsConfig
.
map
.
maxZoom
,
maxZoom
:
GpsConfig
.
map
.
maxZoom
,
pitch
:
GpsConfig
.
map
.
defaultPitch
,
// 初始倾斜角度,0 表示 2D 模式
pitch
:
GpsConfig
.
map
.
defaultPitch
,
antialias
:
true
,
// 抗锯齿
antialias
:
true
,
bearing
:
0
,
// 初始旋转角度
bearing
:
0
,
interactive
:
true
,
// 可交互
interactive
:
true
,
attributionControl
:
false
,
attributionControl
:
false
,
minTileCacheSize
:
GpsConfig
.
map
.
minTileCacheSize
,
minTileCacheSize
:
GpsConfig
.
map
.
minTileCacheSize
,
maxTileCacheSize
:
GpsConfig
.
map
.
maxTileCacheSize
,
// 调整瓦片缓存大小
maxTileCacheSize
:
GpsConfig
.
map
.
maxTileCacheSize
,
transformRequest
:
(
url
,
resourceType
)
=>
{
if
(
resourceType
===
'Tile'
)
{
return
{
url
:
transformTencentSatelliteUrl
(
url
)
};
}
return
{
url
};
},
});
});
// 添加resize事件监听器
window
.
addEventListener
(
'resize'
,
()
=>
{
window
.
addEventListener
(
'resize'
,
()
=>
{
map
.
value
.
resize
();
map
.
value
.
resize
();
});
});
// 监听 pitchChange 事件
map
.
value
.
on
(
'load'
,
()
=>
{
map
.
value
.
on
(
'load'
,
()
=>
{
loadMapIcons
().
then
(()
=>
{
loadMapIcons
().
then
(()
=>
{
refreshLiveMap
();
refreshLiveMap
();
...
@@ -573,6 +734,18 @@
...
@@ -573,6 +734,18 @@
},
0
);
},
0
);
}
}
// 切换底图
const
applyMapStyle
=
()
=>
{
if
(
!
map
.
value
)
return
;
map
.
value
.
setStyle
(
getMapStyle
(
mapLayer
.
value
,
mapType
.
value
));
map
.
value
.
once
(
'style.load'
,
()
=>
{
loadMapIcons
().
then
(()
=>
{
refreshLiveMap
();
destroyEvent
();
initMapEvent
();
});
});
}
/**
/**
* 刷新地图
* 刷新地图
*/
*/
...
@@ -608,7 +781,7 @@
...
@@ -608,7 +781,7 @@
* */
* */
const
initCenter
=
()
=>
{
const
initCenter
=
()
=>
{
const
allCoordinates
=
trackCoordinates
.
value
.
map
(
const
allCoordinates
=
trackCoordinates
.
value
.
map
(
(
v
)
=>
[
v
[
0
],
v
[
1
]]
,
(
v
)
=>
transformCoord
(
v
[
0
],
v
[
1
])
,
);
);
if
(
allCoordinates
.
length
>
0
)
{
if
(
allCoordinates
.
length
>
0
)
{
const
bounds
=
allCoordinates
?.
reduce
(
const
bounds
=
allCoordinates
?.
reduce
(
...
@@ -699,7 +872,7 @@
...
@@ -699,7 +872,7 @@
type
:
'Feature'
,
type
:
'Feature'
,
geometry
:
{
geometry
:
{
type
:
'Point'
,
type
:
'Point'
,
coordinates
:
[
longitude
,
latitude
]
,
coordinates
:
transformCoord
(
longitude
,
latitude
)
,
},
},
properties
:
{
properties
:
{
text
:
name
,
text
:
name
,
...
@@ -871,10 +1044,7 @@
...
@@ -871,10 +1044,7 @@
*/
*/
const
initMapControls
=
()
=>
{
const
initMapControls
=
()
=>
{
if
(
!
map
.
value
)
return
;
if
(
!
map
.
value
)
return
;
// 添加比例尺控件到地图
map
.
value
.
addControl
(
new
mapboxgl
.
ScaleControl
(),
'bottom-right'
);
map
.
value
.
addControl
(
new
mapboxgl
.
ScaleControl
(),
'bottom-right'
);
// 将搜索框添加到地图
map
.
value
.
addControl
(
geocoder
,
'top-left'
);
};
};
/**
/**
...
@@ -891,7 +1061,7 @@
...
@@ -891,7 +1061,7 @@
return
new
Promise
((
resolve
,
reject
)
=>
{
return
new
Promise
((
resolve
,
reject
)
=>
{
map
.
value
?.
loadImage
(
icon
,
(
error
,
image
)
=>
{
map
.
value
?.
loadImage
(
icon
,
(
error
,
image
)
=>
{
if
(
error
)
{
if
(
error
)
{
// console.error(
`Failed to load image: ${icon}`
, error);
// console.error(
'Failed to load image: ' + icon
, error);
reject
(
error
);
reject
(
error
);
}
else
{
}
else
{
if
(
map
.
value
?.
hasImage
(
icon
))
{
if
(
map
.
value
?.
hasImage
(
icon
))
{
...
@@ -947,10 +1117,11 @@
...
@@ -947,10 +1117,11 @@
layers
:
[
MapKeyEnum
.
CP_ID
]
layers
:
[
MapKeyEnum
.
CP_ID
]
});
});
if
(
features
.
length
)
return
if
(
features
.
length
)
return
// 获取点击位置的坐标
// 获取点击位置的坐标
(高德/腾讯底图下为 GCJ-02,需反向转换为 WGS-84 存储)
const
coordinates
=
e
.
lngLat
;
const
coordinates
=
e
.
lngLat
;
markerForm
.
latitude
=
coordinates
.
lat
;
const
[
wgsLng
,
wgsLat
]
=
reverseTransformCoord
(
coordinates
.
lng
,
coordinates
.
lat
);
markerForm
.
longitude
=
coordinates
.
lng
;
markerForm
.
latitude
=
wgsLat
;
markerForm
.
longitude
=
wgsLng
;
markerForm
.
name
=
'新增标记点'
markerForm
.
name
=
'新增标记点'
editCheckPoint
(
null
)
editCheckPoint
(
null
)
...
@@ -1010,12 +1181,12 @@
...
@@ -1010,12 +1181,12 @@
if
(
fileType
===
'gpx'
||
fileType
===
'kml'
)
{
if
(
fileType
===
'gpx'
||
fileType
===
'kml'
)
{
if
(
file
.
size
<=
10
*
1024
*
1024
)
{
if
(
file
.
size
<=
10
*
1024
*
1024
)
{
// 检查文件大小不超过 10M
// 检查文件大小不超过 10M
message
.
success
(
`
${
file
.
name
}
文件选择成功`
);
message
.
success
(
file
.
name
+
' 文件选择成功'
);
}
else
{
}
else
{
message
.
error
(
`
${
file
.
name
}
文件大小超过 10M,请重新选择`
);
message
.
error
(
file
.
name
+
' 文件大小超过 10M,请重新选择'
);
}
}
}
else
{
}
else
{
message
.
error
(
`只能上传 GPX 或 KML 文件,请重新选择`
);
message
.
error
(
'只能上传 GPX 或 KML 文件,请重新选择'
);
}
}
if
(
file
)
{
if
(
file
)
{
fileName
.
value
=
file
.
name
;
fileName
.
value
=
file
.
name
;
...
@@ -1276,10 +1447,93 @@
...
@@ -1276,10 +1447,93 @@
// 回退
// 回退
window
.
history
.
back
()
window
.
history
.
back
()
}
}
// 切换底图
// 切换底图(已移至 applyMapStyle)
const
toggleMapLayer
=
()
=>
{
/**------------------------ 搜索 start ------------------------**/
map
.
value
.
setStyle
(
mapStyle
.
value
);
const
onSearchInput
=
()
=>
{
};
clearTimeout
(
searchTimer
);
if
(
!
searchKeyword
.
value
.
trim
())
{
searchResults
.
value
=
[];
showSearchResults
.
value
=
false
;
return
;
}
var
now
=
Date
.
now
();
var
elapsed
=
now
-
lastSearchTime
;
var
THROTTLE_MS
=
1000
;
if
(
elapsed
>=
THROTTLE_MS
)
{
lastSearchTime
=
now
;
doSearch
();
}
else
{
searchTimer
=
setTimeout
(
function
()
{
lastSearchTime
=
Date
.
now
();
doSearch
();
},
THROTTLE_MS
-
elapsed
);
}
}
const
doSearch
=
async
()
=>
{
const
keyword
=
searchKeyword
.
value
.
trim
();
if
(
!
keyword
)
return
;
searchLoading
.
value
=
true
;
try
{
const
bounds
=
map
.
value
.
getBounds
();
const
mapBound
=
bounds
.
getWest
()
+
','
+
bounds
.
getSouth
()
+
','
+
bounds
.
getEast
()
+
','
+
bounds
.
getNorth
();
const
level
=
String
(
Math
.
round
(
map
.
value
.
getZoom
()));
const
postStr
=
JSON
.
stringify
({
keyWord
:
keyword
,
level
,
mapBound
,
queryType
:
'1'
,
start
:
'0'
,
count
:
'10'
,
});
const
url
=
'https://api.tianditu.gov.cn/v2/search?postStr='
+
encodeURIComponent
(
postStr
)
+
'&type=query&tk='
+
tk
;
const
res
=
await
fetch
(
url
).
then
(
r
=>
r
.
json
());
var
results
=
[];
if
(
res
.
pois
&&
res
.
pois
.
length
)
{
results
=
res
.
pois
.
map
(
function
(
poi
)
{
var
parts
=
poi
.
lonlat
.
split
(
/
[
,
]
+/
).
map
(
Number
);
return
{
name
:
poi
.
name
,
address
:
poi
.
address
||
''
,
lng
:
parts
[
0
],
lat
:
parts
[
1
]
};
});
}
else
if
(
res
.
statistics
&&
res
.
statistics
.
priorityCitys
&&
res
.
statistics
.
priorityCitys
.
length
)
{
results
=
res
.
statistics
.
priorityCitys
.
map
(
function
(
city
)
{
var
parts
=
city
.
lonlat
.
split
(
','
).
map
(
Number
);
return
{
name
:
city
.
adminName
,
address
:
'共'
+
city
.
count
+
'条结果'
,
lng
:
parts
[
0
],
lat
:
parts
[
1
]
};
});
}
if
(
results
.
length
)
{
searchResults
.
value
=
results
;
showSearchResults
.
value
=
true
;
}
else
{
searchResults
.
value
=
[];
showSearchResults
.
value
=
false
;
message
.
warning
(
'未找到相关地点'
);
}
}
catch
(
err
)
{
console
.
error
(
'搜索失败:'
,
err
);
message
.
error
(
'搜索失败,请重试'
);
}
finally
{
searchLoading
.
value
=
false
;
}
}
const
onSelectSearchResult
=
(
item
)
=>
{
showSearchResults
.
value
=
false
;
searchKeyword
.
value
=
item
.
name
;
const
[
flyLng
,
flyLat
]
=
transformCoord
(
item
.
lng
,
item
.
lat
);
map
.
value
.
flyTo
({
center
:
[
flyLng
,
flyLat
],
zoom
:
15
,
duration
:
1000
,
});
}
const
flyToMarker
=
(
marker
)
=>
{
if
(
!
map
.
value
||
!
marker
.
longitude
||
!
marker
.
latitude
)
return
;
var
coords
=
transformCoord
(
Number
(
marker
.
longitude
),
Number
(
marker
.
latitude
));
map
.
value
.
flyTo
({
center
:
coords
,
zoom
:
16
,
duration
:
800
,
});
}
/**------------------------ 搜索 end ------------------------**/
/**------------------------ marker end ------------------------**/
/**------------------------ marker end ------------------------**/
// 对外返回的信息
// 对外返回的信息
return
{
return
{
...
@@ -1290,6 +1544,8 @@
...
@@ -1290,6 +1544,8 @@
// 公共
// 公共
CheckPointTypeEnum
,
CheckPointTypeEnum
,
StatusEnum
,
StatusEnum
,
MapLayerEnum
,
MapTypeEnum
,
// 点位编辑模块
// 点位编辑模块
markers
,
markers
,
markerForm
,
markerForm
,
...
@@ -1304,8 +1560,17 @@
...
@@ -1304,8 +1560,17 @@
isAddMode
,
isAddMode
,
fileList
,
fileList
,
onBack
,
onBack
,
isSatelliteLayer
,
mapLayer
,
toggleMapLayer
,
mapType
,
applyMapStyle
,
searchKeyword
,
searchResults
,
searchLoading
,
showSearchResults
,
onSearchInput
,
doSearch
,
onSelectSearchResult
,
flyToMarker
,
uploadUrl
,
uploadUrl
,
uploadFinish
uploadFinish
}
}
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment