init
This commit is contained in:
450
app/src/main/assets/gis_map.html
Normal file
450
app/src/main/assets/gis_map.html
Normal file
@ -0,0 +1,450 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
|
||||
<title>GIS管网管理</title>
|
||||
<!-- OpenLayers CSS -->
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/ol@v7.4.0/ol.css">
|
||||
<style>
|
||||
body, html { margin: 0; padding: 0; height: 100%; width: 100%; overflow: hidden; font-family: sans-serif; }
|
||||
#map { width: 100%; height: 100%; }
|
||||
|
||||
/* UI Controls */
|
||||
.ui-panel {
|
||||
position: absolute;
|
||||
top: 10px;
|
||||
right: 10px;
|
||||
background: rgba(255, 255, 255, 0.9);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
box-shadow: 0 2px 6px rgba(0,0,0,0.3);
|
||||
z-index: 1000;
|
||||
max-width: 200px;
|
||||
}
|
||||
.ui-group { margin-bottom: 10px; border-bottom: 1px solid #eee; padding-bottom: 5px; }
|
||||
.ui-group:last-child { border-bottom: none; }
|
||||
.ui-title { font-weight: bold; font-size: 14px; margin-bottom: 5px; }
|
||||
button {
|
||||
display: block;
|
||||
width: 100%;
|
||||
margin: 2px 0;
|
||||
padding: 5px;
|
||||
font-size: 12px;
|
||||
cursor: pointer;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 2px;
|
||||
}
|
||||
button:hover { background: #0056b3; }
|
||||
button.active { background: #004494; border: 2px solid #002a5c; }
|
||||
select { width: 100%; padding: 5px; margin-bottom: 5px; }
|
||||
|
||||
/* Popup */
|
||||
.ol-popup {
|
||||
position: absolute;
|
||||
background-color: white;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.2);
|
||||
padding: 15px;
|
||||
border-radius: 10px;
|
||||
border: 1px solid #cccccc;
|
||||
bottom: 12px;
|
||||
left: -50px;
|
||||
min-width: 200px;
|
||||
}
|
||||
.ol-popup:after, .ol-popup:before {
|
||||
top: 100%;
|
||||
border: solid transparent;
|
||||
content: " ";
|
||||
height: 0;
|
||||
width: 0;
|
||||
position: absolute;
|
||||
pointer-events: none;
|
||||
}
|
||||
.ol-popup:after {
|
||||
border-top-color: white;
|
||||
border-width: 10px;
|
||||
left: 48px;
|
||||
margin-left: -10px;
|
||||
}
|
||||
.ol-popup:before {
|
||||
border-top-color: #cccccc;
|
||||
border-width: 11px;
|
||||
left: 48px;
|
||||
margin-left: -11px;
|
||||
}
|
||||
.ol-popup-closer {
|
||||
text-decoration: none;
|
||||
position: absolute;
|
||||
top: 2px;
|
||||
right: 8px;
|
||||
color: #999;
|
||||
}
|
||||
.ol-popup-closer:after { content: "✖"; }
|
||||
|
||||
/* Legend */
|
||||
.legend {
|
||||
position: absolute;
|
||||
bottom: 20px;
|
||||
left: 10px;
|
||||
background: rgba(255,255,255,0.8);
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
z-index: 1000;
|
||||
font-size: 12px;
|
||||
}
|
||||
.legend-item { display: flex; align-items: center; margin-bottom: 5px; }
|
||||
.color-box { width: 20px; height: 5px; margin-right: 5px; }
|
||||
.point-circle { width: 10px; height: 10px; border-radius: 50%; margin-right: 5px; }
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
|
||||
<!-- Popup Overlay -->
|
||||
<div id="popup" class="ol-popup">
|
||||
<a href="#" id="popup-closer" class="ol-popup-closer"></a>
|
||||
<div id="popup-content"></div>
|
||||
</div>
|
||||
|
||||
<!-- UI Panel -->
|
||||
<div class="ui-panel">
|
||||
<div class="ui-group">
|
||||
<div class="ui-title">底图切换</div>
|
||||
<select id="baseLayerSelect" onchange="switchBaseLayer(this.value)">
|
||||
<option value="OSM">OpenStreetMap</option>
|
||||
<option value="GAODE">高德地图</option>
|
||||
<option value="TIANDITU">天地图 (矢量)</option>
|
||||
<option value="TIANDITU_IMG">天地图 (影像)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="ui-group">
|
||||
<div class="ui-title">工具箱</div>
|
||||
<button onclick="toggleMeasure('LineString')">测距</button>
|
||||
<button onclick="toggleMeasure('Polygon')">测面</button>
|
||||
<button onclick="clearMeasure()">清除测量</button>
|
||||
</div>
|
||||
<div class="ui-group">
|
||||
<div class="ui-title">管网维护</div>
|
||||
<button id="btn-add-pipe" onclick="startEdit('Pipe')">新增管线</button>
|
||||
<button id="btn-add-station" onclick="startEdit('Station')">新增泵站</button>
|
||||
<button onclick="stopEdit()">结束编辑</button>
|
||||
<button onclick="deleteSelected()">删除选中</button>
|
||||
</div>
|
||||
<div class="ui-group">
|
||||
<div class="ui-title">数据管理</div>
|
||||
<button onclick="importCAD()">导入CAD (模拟)</button>
|
||||
<button onclick="exportData()">导出数据</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="legend">
|
||||
<div class="ui-title">图例</div>
|
||||
<div class="legend-item"><div class="color-box" style="background:blue;"></div>供水管线</div>
|
||||
<div class="legend-item"><div class="color-box" style="background:red;"></div>污水管线</div>
|
||||
<div class="legend-item"><div class="point-circle" style="background:green;"></div>正常泵站</div>
|
||||
<div class="legend-item"><div class="point-circle" style="background:gray;"></div>离线泵站</div>
|
||||
</div>
|
||||
|
||||
<!-- OpenLayers JS -->
|
||||
<script src="https://cdn.jsdelivr.net/npm/ol@v7.4.0/dist/ol.js"></script>
|
||||
<script>
|
||||
// --- Configuration ---
|
||||
// Note: In a real app, API keys should be secure or proxy-based.
|
||||
const TIANDITU_KEY = 'YOUR_TIANDITU_KEY_HERE'; // Replace with valid key
|
||||
|
||||
// --- Map Initialization ---
|
||||
const map = new ol.Map({
|
||||
target: 'map',
|
||||
view: new ol.View({
|
||||
center: ol.proj.fromLonLat([121.47, 31.23]), // Shanghai
|
||||
zoom: 12
|
||||
}),
|
||||
controls: ol.control.defaults.defaults().extend([
|
||||
new ol.control.ScaleLine(),
|
||||
new ol.control.FullScreen()
|
||||
])
|
||||
});
|
||||
|
||||
// --- Base Layers ---
|
||||
const baseLayers = {
|
||||
'OSM': new ol.layer.Tile({
|
||||
source: new ol.source.OSM(),
|
||||
visible: true
|
||||
}),
|
||||
'GAODE': new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: 'https://wprd0{1-4}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&style=7&x={x}&y={y}&z={z}'
|
||||
}),
|
||||
visible: false
|
||||
}),
|
||||
'TIANDITU': new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: `http://t{0-7}.tianditu.gov.cn/DataServer?T=vec_w&x={x}&y={y}&l={z}&tk=${TIANDITU_KEY}`
|
||||
}),
|
||||
visible: false
|
||||
}),
|
||||
'TIANDITU_IMG': new ol.layer.Tile({
|
||||
source: new ol.source.XYZ({
|
||||
url: `http://t{0-7}.tianditu.gov.cn/DataServer?T=img_w&x={x}&y={y}&l={z}&tk=${TIANDITU_KEY}`
|
||||
}),
|
||||
visible: false
|
||||
})
|
||||
};
|
||||
|
||||
for (let key in baseLayers) {
|
||||
map.addLayer(baseLayers[key]);
|
||||
}
|
||||
|
||||
function switchBaseLayer(type) {
|
||||
for (let key in baseLayers) {
|
||||
baseLayers[key].setVisible(key === type);
|
||||
}
|
||||
}
|
||||
|
||||
// --- Business Layers (Pipe & Station) ---
|
||||
const vectorSource = new ol.source.Vector();
|
||||
const vectorLayer = new ol.layer.Vector({
|
||||
source: vectorSource,
|
||||
style: function(feature) {
|
||||
const type = feature.get('type');
|
||||
const status = feature.get('status');
|
||||
|
||||
if (type === 'Pipe') {
|
||||
return new ol.style.Style({
|
||||
stroke: new ol.style.Stroke({
|
||||
color: feature.get('pipeType') === 'Sewage' ? 'red' : 'blue',
|
||||
width: 3
|
||||
})
|
||||
});
|
||||
} else if (type === 'Station') {
|
||||
return new ol.style.Style({
|
||||
image: new ol.style.Circle({
|
||||
radius: 7,
|
||||
fill: new ol.style.Fill({ color: status === 'Online' ? 'green' : 'gray' }),
|
||||
stroke: new ol.style.Stroke({ color: 'white', width: 2 })
|
||||
}),
|
||||
text: new ol.style.Text({
|
||||
text: feature.get('name'),
|
||||
offsetY: -15,
|
||||
fill: new ol.style.Fill({ color: '#000' }),
|
||||
stroke: new ol.style.Stroke({ color: '#fff', width: 2 })
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
map.addLayer(vectorLayer);
|
||||
|
||||
// --- Mock Data ---
|
||||
function loadMockData() {
|
||||
// Pipe
|
||||
const pipe = new ol.Feature({
|
||||
geometry: new ol.geom.LineString([
|
||||
ol.proj.fromLonLat([121.46, 31.23]),
|
||||
ol.proj.fromLonLat([121.48, 31.23])
|
||||
]),
|
||||
type: 'Pipe',
|
||||
pipeType: 'Water',
|
||||
id: 'P001'
|
||||
});
|
||||
// Station
|
||||
const station = new ol.Feature({
|
||||
geometry: new ol.geom.Point(ol.proj.fromLonLat([121.47, 31.235])),
|
||||
type: 'Station',
|
||||
name: '1号泵站',
|
||||
status: 'Online',
|
||||
flow: '500m³/h',
|
||||
level: '3.5m'
|
||||
});
|
||||
vectorSource.addFeatures([pipe, station]);
|
||||
}
|
||||
loadMockData();
|
||||
|
||||
// --- Interaction: Popup ---
|
||||
const container = document.getElementById('popup');
|
||||
const content = document.getElementById('popup-content');
|
||||
const closer = document.getElementById('popup-closer');
|
||||
|
||||
const overlay = new ol.Overlay({
|
||||
element: container,
|
||||
autoPan: true,
|
||||
autoPanAnimation: { duration: 250 }
|
||||
});
|
||||
map.addOverlay(overlay);
|
||||
|
||||
closer.onclick = function() {
|
||||
overlay.setPosition(undefined);
|
||||
closer.blur();
|
||||
return false;
|
||||
};
|
||||
|
||||
map.on('singleclick', function(evt) {
|
||||
if (drawInteraction) return; // Don't popup when drawing
|
||||
|
||||
const feature = map.forEachFeatureAtPixel(evt.pixel, function(feature) {
|
||||
return feature;
|
||||
});
|
||||
|
||||
if (feature) {
|
||||
const coordinate = evt.coordinate;
|
||||
const type = feature.get('type');
|
||||
let html = '';
|
||||
if (type === 'Station') {
|
||||
html = `<b>${feature.get('name')}</b><br>
|
||||
状态: ${feature.get('status')}<br>
|
||||
流量: ${feature.get('flow')}<br>
|
||||
液位: ${feature.get('level')}`;
|
||||
} else if (type === 'Pipe') {
|
||||
html = `<b>管线 ID: ${feature.get('id')}</b><br>
|
||||
类型: ${feature.get('pipeType') === 'Sewage' ? '污水' : '供水'}`;
|
||||
}
|
||||
content.innerHTML = html;
|
||||
overlay.setPosition(coordinate);
|
||||
} else {
|
||||
overlay.setPosition(undefined);
|
||||
}
|
||||
});
|
||||
|
||||
// --- Interaction: Draw & Modify ---
|
||||
let drawInteraction;
|
||||
const modifyInteraction = new ol.interaction.Modify({ source: vectorSource });
|
||||
const selectInteraction = new ol.interaction.Select();
|
||||
|
||||
map.addInteraction(modifyInteraction);
|
||||
map.addInteraction(selectInteraction);
|
||||
|
||||
function startEdit(type) {
|
||||
stopEdit(); // Clear previous
|
||||
|
||||
let geometryType = type === 'Station' ? 'Point' : 'LineString';
|
||||
drawInteraction = new ol.interaction.Draw({
|
||||
source: vectorSource,
|
||||
type: geometryType
|
||||
});
|
||||
|
||||
drawInteraction.on('drawend', function(evt) {
|
||||
const feature = evt.feature;
|
||||
feature.set('type', type);
|
||||
if (type === 'Station') {
|
||||
feature.set('name', '新泵站');
|
||||
feature.set('status', 'Online');
|
||||
} else {
|
||||
feature.set('pipeType', 'Water');
|
||||
feature.set('id', 'New-' + Date.now());
|
||||
}
|
||||
});
|
||||
|
||||
map.addInteraction(drawInteraction);
|
||||
|
||||
// UI feedback
|
||||
document.querySelectorAll('button').forEach(b => b.classList.remove('active'));
|
||||
if(type === 'Pipe') document.getElementById('btn-add-pipe').classList.add('active');
|
||||
if(type === 'Station') document.getElementById('btn-add-station').classList.add('active');
|
||||
}
|
||||
|
||||
function stopEdit() {
|
||||
if (drawInteraction) {
|
||||
map.removeInteraction(drawInteraction);
|
||||
drawInteraction = null;
|
||||
}
|
||||
document.querySelectorAll('button').forEach(b => b.classList.remove('active'));
|
||||
}
|
||||
|
||||
function deleteSelected() {
|
||||
const selectedFeatures = selectInteraction.getFeatures();
|
||||
selectedFeatures.forEach(function(feature) {
|
||||
vectorSource.removeFeature(feature);
|
||||
});
|
||||
selectedFeatures.clear();
|
||||
overlay.setPosition(undefined);
|
||||
}
|
||||
|
||||
// --- Tools: Measure ---
|
||||
let measureDraw;
|
||||
function toggleMeasure(type) {
|
||||
stopEdit();
|
||||
if (measureDraw) map.removeInteraction(measureDraw);
|
||||
|
||||
measureDraw = new ol.interaction.Draw({
|
||||
source: new ol.source.Vector(),
|
||||
type: type
|
||||
});
|
||||
|
||||
measureDraw.on('drawstart', function(evt) {
|
||||
// Simplified measurement logic for demo
|
||||
// In real app, create tooltips and calculate length/area using ol.sphere
|
||||
});
|
||||
|
||||
measureDraw.on('drawend', function(evt) {
|
||||
const geom = evt.feature.getGeometry();
|
||||
let output;
|
||||
if (geom instanceof ol.geom.Polygon) {
|
||||
output = '面积: ' + (geom.getArea() / 1000000).toFixed(2) + ' km²';
|
||||
} else if (geom instanceof ol.geom.LineString) {
|
||||
output = '距离: ' + (geom.getLength() / 1000).toFixed(2) + ' km';
|
||||
}
|
||||
alert(output);
|
||||
map.removeInteraction(measureDraw); // Stop after one measure
|
||||
});
|
||||
|
||||
map.addInteraction(measureDraw);
|
||||
}
|
||||
|
||||
function clearMeasure() {
|
||||
// Logic to clear measurement overlays would go here
|
||||
if (measureDraw) map.removeInteraction(measureDraw);
|
||||
}
|
||||
|
||||
// --- Data Import/Export ---
|
||||
function importCAD() {
|
||||
// Mock implementation
|
||||
// In reality, this would trigger a file input, upload to server,
|
||||
// server converts DWG -> GeoJSON, returns GeoJSON, then:
|
||||
// const format = new ol.format.GeoJSON();
|
||||
// const features = format.readFeatures(jsonData);
|
||||
// vectorSource.addFeatures(features);
|
||||
alert("模拟导入CAD图纸成功!已转换为GeoJSON并加载。");
|
||||
|
||||
// Add a mock feature representing CAD import
|
||||
const cadFeature = new ol.Feature({
|
||||
geometry: new ol.geom.LineString([
|
||||
ol.proj.fromLonLat([121.465, 31.225]),
|
||||
ol.proj.fromLonLat([121.475, 31.225])
|
||||
]),
|
||||
type: 'Pipe',
|
||||
pipeType: 'Sewage',
|
||||
id: 'CAD-001'
|
||||
});
|
||||
vectorSource.addFeature(cadFeature);
|
||||
}
|
||||
|
||||
function exportData() {
|
||||
const format = new ol.format.GeoJSON();
|
||||
const features = vectorSource.getFeatures();
|
||||
const json = format.writeFeatures(features);
|
||||
console.log(json);
|
||||
alert("数据已导出 (查看控制台)");
|
||||
// Can implement file download here
|
||||
}
|
||||
|
||||
// --- Android Interface ---
|
||||
// If loaded in Android WebView, this allows Android to call JS
|
||||
window.locateDevice = function(lat, lon) {
|
||||
const coords = ol.proj.fromLonLat([lon, lat]);
|
||||
map.getView().animate({ center: coords, zoom: 15 });
|
||||
|
||||
// Add marker for user location
|
||||
const userFeature = new ol.Feature({
|
||||
geometry: new ol.geom.Point(coords),
|
||||
type: 'Station', // Reuse station style or define new
|
||||
name: '当前位置',
|
||||
status: 'Online'
|
||||
});
|
||||
vectorSource.addFeature(userFeature);
|
||||
};
|
||||
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user