From 70fde211f1538b61f2e4378ca2a46cecaac029f6 Mon Sep 17 00:00:00 2001 From: "Shuya.Guan20" Date: Wed, 4 Dec 2024 13:59:50 -0500 Subject: [PATCH 1/2] Initial commit for Automated Vehicle Crowdsourcing project --- assests/icons | 0 css/cyberpunk.css | 230 ++++++++++++++++++++++++++++++++++++++++++ css/main.css | 249 ++++++++++++++++++++++++++++++++++++++++++++++ index.html | 101 +++++++++++++++++++ js/map.js | 162 ++++++++++++++++++++++++++++++ js/markers.js | 232 ++++++++++++++++++++++++++++++++++++++++++ js/ui.js | 232 ++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 1206 insertions(+) create mode 100644 assests/icons create mode 100644 css/cyberpunk.css create mode 100644 css/main.css create mode 100644 index.html create mode 100644 js/map.js create mode 100644 js/markers.js create mode 100644 js/ui.js diff --git a/assests/icons b/assests/icons new file mode 100644 index 0000000..e69de29 diff --git a/css/cyberpunk.css b/css/cyberpunk.css new file mode 100644 index 0000000..96c6bb0 --- /dev/null +++ b/css/cyberpunk.css @@ -0,0 +1,230 @@ +:root { + --neon-blue: #00FFFF; + --neon-purple: #FF00FF; + --neon-green: #00FF00; + --dark-bg: #0B1A2F; + --glow: 0 0 10px var(--neon-blue); + --glow-purple: 0 0 10px var(--neon-purple); + --glow-strong: 0 0 20px var(--neon-blue); + } + + /* 全局动画效果 */ + @keyframes neonGlow { + 0% { box-shadow: 0 0 5px var(--neon-blue); } + 50% { box-shadow: 0 0 20px var(--neon-blue); } + 100% { box-shadow: 0 0 5px var(--neon-blue); } + } + + @keyframes textGlow { + 0% { text-shadow: 0 0 5px var(--neon-blue); } + 50% { text-shadow: 0 0 20px var(--neon-blue); } + 100% { text-shadow: 0 0 5px var(--neon-blue); } + } + + /* 赛博朋克按钮样式 */ + .cyberpunk-button { + background: transparent; + border: 1px solid var(--neon-blue); + color: var(--neon-blue); + padding: 10px 20px; + cursor: pointer; + transition: all 0.3s ease; + text-transform: uppercase; + font-size: 14px; + letter-spacing: 2px; + position: relative; + overflow: hidden; + margin: 5px; + border-radius: 3px; + } + + .cyberpunk-button:hover { + background: var(--neon-blue); + color: var(--dark-bg); + box-shadow: var(--glow-strong); + transform: translateY(-2px); + } + + .cyberpunk-button:active { + transform: translateY(1px); + } + + .cyberpunk-button.cancel { + border-color: var(--neon-purple); + color: var(--neon-purple); + } + + .cyberpunk-button.cancel:hover { + background: var(--neon-purple); + color: var(--dark-bg); + box-shadow: var(--glow-purple); + } + + /* 赛博朋克面板样式 */ + .cyberpunk-panel { + box-shadow: var(--glow); + backdrop-filter: blur(5px); + animation: neonGlow 2s infinite; + } + + .cyberpunk-form { + box-shadow: var(--glow); + backdrop-filter: blur(10px); + } + + .cyberpunk-form h2 { + color: var(--neon-blue); + text-transform: uppercase; + letter-spacing: 3px; + text-align: center; + margin-bottom: 20px; + animation: textGlow 2s infinite; + font-size: 24px; + } + + /* Select 下拉菜单样式 */ + .cyberpunk-select { + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + background: rgba(0, 0, 0, 0.7); + border: 1px solid var(--neon-blue); + border-radius: 4px; + padding: 10px 35px 10px 10px; + color: #fff; + width: 100%; + cursor: pointer; + font-size: 14px; + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2300FFFF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + background-size: 16px; + transition: all 0.3s ease; + } + + .cyberpunk-select:hover { + border-color: var(--neon-blue); + box-shadow: 0 0 10px rgba(0, 255, 255, 0.3); + } + + .cyberpunk-select:focus { + outline: none; + border-color: var(--neon-purple); + box-shadow: 0 0 15px rgba(0, 255, 255, 0.5); + } + + /* Description textarea styles */ + .cyberpunk-textarea { + background: rgba(0, 0, 0, 0.7) !important; + color: #fff !important; + transition: all 0.3s ease; + border: 1px solid var(--neon-blue); + padding: 10px; + border-radius: 4px; + width: 100%; + min-height: 100px; + resize: vertical; + font-family: 'Arial', sans-serif; + } + + .cyberpunk-textarea[readonly] { + cursor: not-allowed; + opacity: 0.7; + border-color: rgba(0, 255, 255, 0.3); + } + + .cyberpunk-textarea:not([readonly]) { + cursor: text; + opacity: 1; + box-shadow: 0 0 10px rgba(0, 255, 255, 0.3); + } + + .cyberpunk-textarea:focus { + outline: none; + border-color: var(--neon-purple); + box-shadow: 0 0 15px rgba(0, 255, 255, 0.5); + } + + .cyberpunk-textarea::placeholder { + color: rgba(0, 255, 255, 0.5); + } + + .cyberpunk-textarea[readonly]::placeholder { + color: rgba(0, 255, 255, 0.3); + } + + .cyberpunk-select option { + background-color: rgba(11, 26, 47, 0.95); + color: #fff; + padding: 10px; + } + + /* 标记项目样式 */ + .marker-item { + border: 1px solid var(--neon-blue); + transition: all 0.3s ease; + position: relative; + overflow: hidden; + } + + .marker-item:hover { + box-shadow: var(--glow); + transform: translateX(5px); + } + + .marker-item::before { + content: ''; + position: absolute; + top: 0; + left: -100%; + width: 100%; + height: 2px; + background: linear-gradient(90deg, transparent, var(--neon-blue)); + animation: animate 2s linear infinite; + } + + /* 动画效果 */ + @keyframes animate { + 0% { left: -100%; } + 50%, 100% { left: 100%; } + } + + /* 通知样式 */ + .notification-container { + position: fixed; + top: 20px; + right: 20px; + z-index: 9999; + } + + .notification { + background: rgba(11, 26, 47, 0.95); + border-left: 4px solid var(--neon-blue); + color: #fff; + padding: 15px 20px; + margin-bottom: 10px; + border-radius: 4px; + box-shadow: var(--glow); + transform: translateX(120%); + transition: transform 0.3s ease; + min-width: 300px; + } + + .notification.show { + transform: translateX(0); + } + + .notification.error { + border-left-color: var(--neon-purple); + } + + /* 临时标记样式 */ + .marker-icon.temporary { + animation: pulseGlow 1s infinite alternate; + } + + @keyframes pulseGlow { + 0% { box-shadow: 0 0 5px rgba(0, 255, 255, 0.5); } + 100% { box-shadow: 0 0 20px rgba(0, 255, 255, 0.8); } + } + \ No newline at end of file diff --git a/css/main.css b/css/main.css new file mode 100644 index 0000000..ed66846 --- /dev/null +++ b/css/main.css @@ -0,0 +1,249 @@ +/* 基础样式重置 */ +* { + margin: 0; + padding: 0; + box-sizing: border-box; + } + + body { + margin: 0; + padding: 0; + background-color: #0B1A2F; + color: #ffffff; + font-family: 'Arial', sans-serif; + overflow: hidden; + } + + /* 地图容器 */ + #map { + position: absolute; + top: 0; + left: 0; + width: 100%; + height: 100vh; + } + + /* 控制面板样式 */ + .cyberpunk-panel { + position: absolute; + background: rgba(11, 26, 47, 0.9); + padding: 20px; + border: 1px solid #00FFFF; + border-radius: 5px; + z-index: 1000; + } + + #control-panel { + top: 20px; + left: 20px; + min-width: 250px; + } + + .layer-toggle { + top: 20px; + right: 20px; + min-width: 200px; + } + + /* 标记列表样式 */ + .marker-list { + margin-top: 20px; + max-height: 400px; + overflow-y: auto; + padding-right: 10px; + } + + .marker-item { + border: 1px solid #00FFFF; + margin: 5px 0; + padding: 10px; + border-radius: 3px; + background: rgba(0, 255, 255, 0.1); + cursor: pointer; + transition: all 0.3s ease; + } + + .marker-item:hover { + transform: translateX(5px); + background: rgba(0, 255, 255, 0.2); + } + + /* 表单样式 */ + .cyberpunk-form { + position: absolute; + top: 50%; + left: 50%; + transform: translate(-50%, -50%); + background: rgba(11, 26, 47, 0.95); + padding: 25px; + border: 1px solid #00FFFF; + border-radius: 5px; + z-index: 1000; + min-width: 350px; + display: none; + backdrop-filter: blur(10px); + } + + .form-group { + margin: 15px 0; + } + + .form-group label { + display: block; + margin-bottom: 8px; + color: #00FFFF; + font-size: 14px; + text-transform: uppercase; + letter-spacing: 1px; + } + + .form-group input, + .form-group textarea { + width: 100%; + padding: 10px; + background: rgba(0, 0, 0, 0.5); + border: 1px solid #00FFFF; + color: #fff; + border-radius: 3px; + font-size: 14px; + } + + .form-group textarea { + height: 100px; + resize: vertical; + } + + /* Select 特定样式 */ + .cyberpunk-select { + width: 100%; + padding: 10px 35px 10px 10px; + background: rgba(0, 0, 0, 0.7); + border: 1px solid #00FFFF; + color: #fff; + border-radius: 4px; + font-size: 14px; + cursor: pointer; + appearance: none; + -webkit-appearance: none; + -moz-appearance: none; + } + + /* Description textarea styles */ + .cyberpunk-textarea { + background: rgba(0, 0, 0, 0.7) !important; + color: #fff !important; + transition: all 0.3s ease; + border: 1px solid var(--neon-blue); + padding: 10px; + border-radius: 4px; + width: 100%; + min-height: 100px; + resize: vertical; + } + + .cyberpunk-textarea[readonly] { + cursor: not-allowed; + opacity: 0.7; + border-color: rgba(0, 255, 255, 0.3); + } + + .cyberpunk-textarea:not([readonly]) { + cursor: text; + opacity: 1; + box-shadow: 0 0 10px rgba(0, 255, 255, 0.3); + } + + .cyberpunk-textarea:focus { + outline: none; + border-color: var(--neon-purple); + box-shadow: 0 0 15px rgba(0, 255, 255, 0.5); + } + + .cyberpunk-textarea::placeholder { + color: rgba(0, 255, 255, 0.5); + } + + .cyberpunk-textarea[readonly]::placeholder { + color: rgba(0, 255, 255, 0.3); + } + + /* 下拉箭头样式 */ + .cyberpunk-select { + background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2300FFFF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); + background-repeat: no-repeat; + background-position: right 10px center; + background-size: 16px; + } + + /* Select 选项样式 */ + .cyberpunk-select option { + background-color: rgba(11, 26, 47, 0.95); + color: #fff; + padding: 10px; + } + + /* 按钮容器 */ + .form-buttons { + display: flex; + justify-content: space-between; + margin-top: 20px; + gap: 10px; + } + + /* 滚动条样式 */ + ::-webkit-scrollbar { + width: 8px; + } + + ::-webkit-scrollbar-track { + background: rgba(0, 0, 0, 0.3); + border-radius: 4px; + } + + ::-webkit-scrollbar-thumb { + background: #00FFFF; + border-radius: 4px; + } + + /* 标记图标样式 */ + .marker-icon { + width: 20px; + height: 20px; + border-radius: 50%; + cursor: pointer; + border: 2px solid #000; + box-shadow: 0 0 10px rgba(0, 255, 255, 0.5); + } + + /* 根据不同类型设置不同颜色 */ + .marker-icon.roadChange { background-color: #00FFFF; } + .marker-icon.construction { background-color: #FF00FF; } + .marker-icon.traffic { background-color: #FFFF00; } + .marker-icon.incident { background-color: #FF0000; } + .marker-icon.hazard { background-color: #FFA500; } + .marker-icon.closure { background-color: #FF0000; } + .marker-icon.newRoad { background-color: #00FF00; } + .marker-icon.infrastructure { background-color: #4169E1; } + .marker-icon.accident { background-color: #FF4500; } + .marker-icon.testing { background-color: #9400D3; } + .marker-icon.parking { background-color: #1E90FF; } + .marker-icon.charging { background-color: #32CD32; } + + /* Layer toggle styles */ + .layer-toggle-item { + margin: 10px 0; + display: flex; + align-items: center; + } + + .layer-toggle-item input[type="checkbox"] { + margin-right: 10px; + } + + .layer-toggle h3 { + margin-bottom: 15px; + color: #00FFFF; + font-size: 16px; + letter-spacing: 1px; + } + \ No newline at end of file diff --git a/index.html b/index.html new file mode 100644 index 0000000..7d01e7a --- /dev/null +++ b/index.html @@ -0,0 +1,101 @@ + + + + + +Auto Driving Map + + + + + + + + + +
+ + +
+ +
+ +
+
+ + +
+

Map Layers

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+

ADD NEW MARKER

+
+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
+
+ + + + + + + diff --git a/js/map.js b/js/map.js new file mode 100644 index 0000000..062b322 --- /dev/null +++ b/js/map.js @@ -0,0 +1,162 @@ +// Mapbox访问令牌 +const mapboxToken = 'pk.eyJ1IjoiZ3NodXlhIiwiYSI6ImNtMHk2bjRodTBrNmIya29obmc2OG5kZTUifQ.TM9gfDCRHU_yorUj8CQHSA'; + +// 初始化地图 +mapboxgl.accessToken = mapboxToken; +const map = new mapboxgl.Map({ +container: 'map', +style: 'mapbox://styles/mapbox/dark-v10', +center: [-122.4194, 37.7749], // 旧金山坐标 +zoom: 12, +minZoom: 2, +maxZoom: 18 +}); + +// 等待地图加载 +map.on('load', () => { +// 添加交通流量图层 +map.addLayer({ +'id': 'traffic-flow', +'type': 'line', +'source': { +'type': 'vector', +'url': 'mapbox://mapbox.mapbox-traffic-v1' +}, +'source-layer': 'traffic', +'paint': { +'line-color': [ +'match', +['get', 'congestion'], +'low', '#00FF00', +'moderate', '#FFFF00', +'heavy', '#FF0000', +'severe', '#FF00FF', +'#FFFFFF' +], +'line-width': 2, +'line-opacity': 0.7 +} +}); + +// 添加3D建筑物图层 +map.addLayer({ +'id': '3d-buildings', +'source': 'composite', +'source-layer': 'building', +'filter': ['==', 'extrude', 'true'], +'type': 'fill-extrusion', +'minzoom': 15, +'paint': { +'fill-extrusion-color': [ +'interpolate', +['linear'], +['get', 'height'], +0, '#0B1A2F', +50, '#00FFFF', +100, '#FF00FF', +200, '#0080FF' +], +'fill-extrusion-height': ['get', 'height'], +'fill-extrusion-base': ['get', 'min_height'], +'fill-extrusion-opacity': 0.5, +'fill-extrusion-vertical-gradient': true +} +}); + +// 添加道路网络图层 +map.addLayer({ +'id': 'road-network', +'type': 'line', +'source': { +'type': 'vector', +'url': 'mapbox://mapbox.mapbox-streets-v8' +}, +'source-layer': 'road', +'paint': { +'line-color': '#00FFFF', +'line-width': [ +'interpolate', +['linear'], +['zoom'], +12, 1, +16, 3 +], +'line-opacity': 0.6 +} +}); + +// 添加兴趣点标签 +map.addLayer({ +'id': 'poi-labels', +'type': 'symbol', +'source': { +'type': 'vector', +'url': 'mapbox://mapbox.mapbox-streets-v8' +}, +'source-layer': 'poi_label', +'layout': { +'text-field': ['get', 'name'], +'text-size': 12, +'text-anchor': 'top', +'text-offset': [0, 1], +'icon-image': ['get', 'maki'], +'icon-size': 1 +}, +'paint': { +'text-color': '#00FFFF', +'text-halo-color': '#000000', +'text-halo-width': 1 +} +}); + +// 添加地铁线路 +map.addLayer({ +'id': 'transit-lines', +'type': 'line', +'source': { +'type': 'vector', +'url': 'mapbox://mapbox.mapbox-streets-v8' +}, +'source-layer': 'transit_line', +'paint': { +'line-color': '#FF00FF', +'line-width': 2, +'line-opacity': 0.8 +} +}); +}); + +// 添加地图控件 +map.addControl(new mapboxgl.NavigationControl(), 'top-right'); +map.addControl(new mapboxgl.ScaleControl({ +maxWidth: 100, +unit: 'metric' +}), 'bottom-left'); + +// 图层切换功能 +document.addEventListener('DOMContentLoaded', () => { +const layers = { +'traffic-flow': 'traffic-flow-toggle', +'poi-labels': 'poi-labels-toggle', +'3d-buildings': '3d-buildings-toggle', +'transit-lines': 'transit-lines-toggle' +}; + +Object.entries(layers).forEach(([layerId, toggleId]) => { +const toggle = document.getElementById(toggleId); +if (toggle) { +toggle.addEventListener('change', (e) => { +const visibility = e.target.checked ? 'visible' : 'none'; +map.setLayoutProperty(layerId, 'visibility', visibility); +}); +} +}); +}); + +// 错误处理 +map.on('error', (e) => { +console.error('Map error:', e.error); +}); + +// 导出地图实例 +window.map = map; diff --git a/js/markers.js b/js/markers.js new file mode 100644 index 0000000..809791b --- /dev/null +++ b/js/markers.js @@ -0,0 +1,232 @@ +// 存储标记数据的数组 +let markers = []; + +// 标记类型对应的颜色和图标 +const markerStyles = { +roadChange: { color: '#00FFFF', icon: '🛣️' }, +construction: { color: '#FF00FF', icon: '🚧' }, +traffic: { color: '#FFFF00', icon: '🚦' }, +incident: { color: '#FF0000', icon: '⚠️' }, +hazard: { color: '#FFA500', icon: '⚠️' }, +closure: { color: '#FF0000', icon: '🚫' }, +newRoad: { color: '#00FF00', icon: '🛣️' }, +infrastructure: { color: '#4169E1', icon: '🏗️' }, +accident: { color: '#FF4500', icon: '💥' }, +testing: { color: '#9400D3', icon: '🚗' }, +parking: { color: '#1E90FF', icon: '🅿️' }, +charging: { color: '#32CD32', icon: '⚡' } +}; + +// 初始化标记功能 +function initializeMarkers() { +const addMarkerBtn = document.getElementById('add-marker-btn'); +const markerForm = document.getElementById('marker-form'); +const addMarkerForm = document.getElementById('add-marker-form'); +const descriptionField = document.getElementById('marker-description'); + +let selectedLocation = null; + +// 点击"Add Marker"按钮显示表单 +addMarkerBtn.addEventListener('click', () => { +markerForm.style.display = 'block'; +descriptionField.readOnly = true; +}); + +// 点击地图选择位置 +map.on('click', (e) => { +selectedLocation = e.lngLat; +showTemporaryMarker(selectedLocation); +markerForm.style.display = 'block'; +descriptionField.readOnly = true; + +// 添加点击动画效果 +const ripple = document.createElement('div'); +ripple.className = 'map-click-ripple'; +ripple.style.left = e.point.x + 'px'; +ripple.style.top = e.point.y + 'px'; +document.body.appendChild(ripple); +setTimeout(() => ripple.remove(), 1000); +}); + +// 取消按钮事件 +document.querySelector('.cancel').addEventListener('click', () => { +markerForm.style.display = 'none'; +removeTemporaryMarker(); +selectedLocation = null; +addMarkerForm.reset(); +descriptionField.readOnly = true; +}); + +// 提交表单 +addMarkerForm.addEventListener('submit', (e) => { +e.preventDefault(); + +// 检查是否有选择位置 +if (!selectedLocation) { +showNotification('Please select a location on the map', 'error'); +return; +} + +// 检查描述是否为空 +if (!descriptionField.value.trim()) { +if (descriptionField.readOnly) { +descriptionField.readOnly = false; +descriptionField.focus(); +showNotification('You can now enter description', 'info'); +return; +} +showNotification('Please enter a description', 'error'); +descriptionField.focus(); +return; +} + +if (descriptionField.readOnly) { +descriptionField.readOnly = false; +descriptionField.focus(); +showNotification('You can now enter description', 'info'); +return; +} + +const newMarker = { +id: Date.now(), +type: document.getElementById('marker-type').value, +description: descriptionField.value, +duration: document.getElementById('marker-duration').value, +location: selectedLocation, +timestamp: new Date().toISOString() +}; + +// 添加标记到地图 +addMarkerToMap(newMarker); + +// 保存标记数据 +markers.push(newMarker); + +// 更新标记列表 +updateMarkerList(); + +// 保存到本地存储 +saveMarkers(); + +// 重置表单和临时标记 +addMarkerForm.reset(); +markerForm.style.display = 'none'; +removeTemporaryMarker(); +selectedLocation = null; +descriptionField.readOnly = true; + +// 显示成功通知 +showNotification('Marker added successfully', 'success'); +}); +} + +// 临时标记变量 +let temporaryMarker = null; + +// 显示临时标记 +function showTemporaryMarker(location) { +removeTemporaryMarker(); + +const el = document.createElement('div'); +el.className = 'marker-icon temporary'; +el.style.backgroundColor = '#FFFFFF'; +el.style.opacity = '0.7'; + +temporaryMarker = new mapboxgl.Marker(el) +.setLngLat(location) +.addTo(map); +} + +// 移除临时标记 +function removeTemporaryMarker() { +if (temporaryMarker) { +temporaryMarker.remove(); +temporaryMarker = null; +} +} + +// 添加标记到地图 +function addMarkerToMap(markerData) { +const style = markerStyles[markerData.type]; + +// 创建标记元素 +const el = document.createElement('div'); +el.className = 'marker-icon'; +el.classList.add(markerData.type); + +// 创建弹出窗口 +const popup = new mapboxgl.Popup({ +offset: 25, +closeButton: true, +closeOnClick: false +}).setHTML(` + +`); + +// 添加标记到地图 +new mapboxgl.Marker(el) +.setLngLat(markerData.location) +.setPopup(popup) +.addTo(map); +} + +// 更新标记列表 +function updateMarkerList() { +const markerList = document.getElementById('marker-list'); +markerList.innerHTML = markers.map(marker => { +const style = markerStyles[marker.type]; +return ` +
+

${style.icon} ${marker.type.toUpperCase()}

+

${marker.description}

+ +Added: ${new Date(marker.timestamp).toLocaleString()}
+Duration: ${marker.duration} +
+
+`; +}).join(''); + +// 添加点击事件 +document.querySelectorAll('.marker-item').forEach(item => { +item.addEventListener('click', () => { +const marker = markers.find(m => m.id === parseInt(item.dataset.id)); +if (marker) { +map.flyTo({ +center: [marker.location.lng, marker.location.lat], +zoom: 15, +duration: 1000 +}); +} +}); +}); +} + +// 加载保存的标记 +function loadSavedMarkers() { +const savedMarkers = localStorage.getItem('markers'); +if (savedMarkers) { +markers = JSON.parse(savedMarkers); +markers.forEach(marker => addMarkerToMap(marker)); +updateMarkerList(); +} +} + +// 保存标记到本地存储 +function saveMarkers() { +localStorage.setItem('markers', JSON.stringify(markers)); +} + +// 初始化 +document.addEventListener('DOMContentLoaded', () => { +initializeMarkers(); +loadSavedMarkers(); +}); + +// 在页面关闭前保存标记 +window.addEventListener('beforeunload', saveMarkers); diff --git a/js/ui.js b/js/ui.js new file mode 100644 index 0000000..809791b --- /dev/null +++ b/js/ui.js @@ -0,0 +1,232 @@ +// 存储标记数据的数组 +let markers = []; + +// 标记类型对应的颜色和图标 +const markerStyles = { +roadChange: { color: '#00FFFF', icon: '🛣️' }, +construction: { color: '#FF00FF', icon: '🚧' }, +traffic: { color: '#FFFF00', icon: '🚦' }, +incident: { color: '#FF0000', icon: '⚠️' }, +hazard: { color: '#FFA500', icon: '⚠️' }, +closure: { color: '#FF0000', icon: '🚫' }, +newRoad: { color: '#00FF00', icon: '🛣️' }, +infrastructure: { color: '#4169E1', icon: '🏗️' }, +accident: { color: '#FF4500', icon: '💥' }, +testing: { color: '#9400D3', icon: '🚗' }, +parking: { color: '#1E90FF', icon: '🅿️' }, +charging: { color: '#32CD32', icon: '⚡' } +}; + +// 初始化标记功能 +function initializeMarkers() { +const addMarkerBtn = document.getElementById('add-marker-btn'); +const markerForm = document.getElementById('marker-form'); +const addMarkerForm = document.getElementById('add-marker-form'); +const descriptionField = document.getElementById('marker-description'); + +let selectedLocation = null; + +// 点击"Add Marker"按钮显示表单 +addMarkerBtn.addEventListener('click', () => { +markerForm.style.display = 'block'; +descriptionField.readOnly = true; +}); + +// 点击地图选择位置 +map.on('click', (e) => { +selectedLocation = e.lngLat; +showTemporaryMarker(selectedLocation); +markerForm.style.display = 'block'; +descriptionField.readOnly = true; + +// 添加点击动画效果 +const ripple = document.createElement('div'); +ripple.className = 'map-click-ripple'; +ripple.style.left = e.point.x + 'px'; +ripple.style.top = e.point.y + 'px'; +document.body.appendChild(ripple); +setTimeout(() => ripple.remove(), 1000); +}); + +// 取消按钮事件 +document.querySelector('.cancel').addEventListener('click', () => { +markerForm.style.display = 'none'; +removeTemporaryMarker(); +selectedLocation = null; +addMarkerForm.reset(); +descriptionField.readOnly = true; +}); + +// 提交表单 +addMarkerForm.addEventListener('submit', (e) => { +e.preventDefault(); + +// 检查是否有选择位置 +if (!selectedLocation) { +showNotification('Please select a location on the map', 'error'); +return; +} + +// 检查描述是否为空 +if (!descriptionField.value.trim()) { +if (descriptionField.readOnly) { +descriptionField.readOnly = false; +descriptionField.focus(); +showNotification('You can now enter description', 'info'); +return; +} +showNotification('Please enter a description', 'error'); +descriptionField.focus(); +return; +} + +if (descriptionField.readOnly) { +descriptionField.readOnly = false; +descriptionField.focus(); +showNotification('You can now enter description', 'info'); +return; +} + +const newMarker = { +id: Date.now(), +type: document.getElementById('marker-type').value, +description: descriptionField.value, +duration: document.getElementById('marker-duration').value, +location: selectedLocation, +timestamp: new Date().toISOString() +}; + +// 添加标记到地图 +addMarkerToMap(newMarker); + +// 保存标记数据 +markers.push(newMarker); + +// 更新标记列表 +updateMarkerList(); + +// 保存到本地存储 +saveMarkers(); + +// 重置表单和临时标记 +addMarkerForm.reset(); +markerForm.style.display = 'none'; +removeTemporaryMarker(); +selectedLocation = null; +descriptionField.readOnly = true; + +// 显示成功通知 +showNotification('Marker added successfully', 'success'); +}); +} + +// 临时标记变量 +let temporaryMarker = null; + +// 显示临时标记 +function showTemporaryMarker(location) { +removeTemporaryMarker(); + +const el = document.createElement('div'); +el.className = 'marker-icon temporary'; +el.style.backgroundColor = '#FFFFFF'; +el.style.opacity = '0.7'; + +temporaryMarker = new mapboxgl.Marker(el) +.setLngLat(location) +.addTo(map); +} + +// 移除临时标记 +function removeTemporaryMarker() { +if (temporaryMarker) { +temporaryMarker.remove(); +temporaryMarker = null; +} +} + +// 添加标记到地图 +function addMarkerToMap(markerData) { +const style = markerStyles[markerData.type]; + +// 创建标记元素 +const el = document.createElement('div'); +el.className = 'marker-icon'; +el.classList.add(markerData.type); + +// 创建弹出窗口 +const popup = new mapboxgl.Popup({ +offset: 25, +closeButton: true, +closeOnClick: false +}).setHTML(` + +`); + +// 添加标记到地图 +new mapboxgl.Marker(el) +.setLngLat(markerData.location) +.setPopup(popup) +.addTo(map); +} + +// 更新标记列表 +function updateMarkerList() { +const markerList = document.getElementById('marker-list'); +markerList.innerHTML = markers.map(marker => { +const style = markerStyles[marker.type]; +return ` +
+

${style.icon} ${marker.type.toUpperCase()}

+

${marker.description}

+ +Added: ${new Date(marker.timestamp).toLocaleString()}
+Duration: ${marker.duration} +
+
+`; +}).join(''); + +// 添加点击事件 +document.querySelectorAll('.marker-item').forEach(item => { +item.addEventListener('click', () => { +const marker = markers.find(m => m.id === parseInt(item.dataset.id)); +if (marker) { +map.flyTo({ +center: [marker.location.lng, marker.location.lat], +zoom: 15, +duration: 1000 +}); +} +}); +}); +} + +// 加载保存的标记 +function loadSavedMarkers() { +const savedMarkers = localStorage.getItem('markers'); +if (savedMarkers) { +markers = JSON.parse(savedMarkers); +markers.forEach(marker => addMarkerToMap(marker)); +updateMarkerList(); +} +} + +// 保存标记到本地存储 +function saveMarkers() { +localStorage.setItem('markers', JSON.stringify(markers)); +} + +// 初始化 +document.addEventListener('DOMContentLoaded', () => { +initializeMarkers(); +loadSavedMarkers(); +}); + +// 在页面关闭前保存标记 +window.addEventListener('beforeunload', saveMarkers); From 0ce5204442415996995cbb3174ba70b0230f2845 Mon Sep 17 00:00:00 2001 From: "Shuya.Guan20" Date: Thu, 19 Dec 2024 12:29:53 -0500 Subject: [PATCH 2/2] Update styles, scripts, and remove unused icons --- assests/icons | 0 css/cyberpunk.css | 14 +++++------- css/main.css | 26 ++++++++++----------- index.html | 10 ++++---- js/map.js | 26 ++++++++++----------- js/markers.js | 58 +++++++++++++++++++++++------------------------ js/ui.js | 58 +++++++++++++++++++++++------------------------ 7 files changed, 95 insertions(+), 97 deletions(-) delete mode 100644 assests/icons diff --git a/assests/icons b/assests/icons deleted file mode 100644 index e69de29..0000000 diff --git a/css/cyberpunk.css b/css/cyberpunk.css index 96c6bb0..d636a75 100644 --- a/css/cyberpunk.css +++ b/css/cyberpunk.css @@ -8,7 +8,6 @@ --glow-strong: 0 0 20px var(--neon-blue); } - /* 全局动画效果 */ @keyframes neonGlow { 0% { box-shadow: 0 0 5px var(--neon-blue); } 50% { box-shadow: 0 0 20px var(--neon-blue); } @@ -21,7 +20,6 @@ 100% { text-shadow: 0 0 5px var(--neon-blue); } } - /* 赛博朋克按钮样式 */ .cyberpunk-button { background: transparent; border: 1px solid var(--neon-blue); @@ -60,7 +58,7 @@ box-shadow: var(--glow-purple); } - /* 赛博朋克面板样式 */ + .cyberpunk-panel { box-shadow: var(--glow); backdrop-filter: blur(5px); @@ -82,7 +80,7 @@ font-size: 24px; } - /* Select 下拉菜单样式 */ + .cyberpunk-select { appearance: none; -webkit-appearance: none; @@ -159,7 +157,7 @@ padding: 10px; } - /* 标记项目样式 */ + .marker-item { border: 1px solid var(--neon-blue); transition: all 0.3s ease; @@ -183,13 +181,13 @@ animation: animate 2s linear infinite; } - /* 动画效果 */ + @keyframes animate { 0% { left: -100%; } 50%, 100% { left: 100%; } } - /* 通知样式 */ + .notification-container { position: fixed; top: 20px; @@ -218,7 +216,7 @@ border-left-color: var(--neon-purple); } - /* 临时标记样式 */ + .marker-icon.temporary { animation: pulseGlow 1s infinite alternate; } diff --git a/css/main.css b/css/main.css index ed66846..0df549f 100644 --- a/css/main.css +++ b/css/main.css @@ -1,4 +1,4 @@ -/* 基础样式重置 */ + * { margin: 0; padding: 0; @@ -14,7 +14,7 @@ overflow: hidden; } - /* 地图容器 */ + #map { position: absolute; top: 0; @@ -23,7 +23,7 @@ height: 100vh; } - /* 控制面板样式 */ + .cyberpunk-panel { position: absolute; background: rgba(11, 26, 47, 0.9); @@ -45,7 +45,7 @@ min-width: 200px; } - /* 标记列表样式 */ + .marker-list { margin-top: 20px; max-height: 400px; @@ -68,7 +68,7 @@ background: rgba(0, 255, 255, 0.2); } - /* 表单样式 */ + .cyberpunk-form { position: absolute; top: 50%; @@ -113,7 +113,7 @@ resize: vertical; } - /* Select 特定样式 */ + .cyberpunk-select { width: 100%; padding: 10px 35px 10px 10px; @@ -128,7 +128,7 @@ -moz-appearance: none; } - /* Description textarea styles */ + .cyberpunk-textarea { background: rgba(0, 0, 0, 0.7) !important; color: #fff !important; @@ -167,7 +167,7 @@ color: rgba(0, 255, 255, 0.3); } - /* 下拉箭头样式 */ + .cyberpunk-select { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2300FFFF' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpolyline points='6 9 12 15 18 9'%3E%3C/polyline%3E%3C/svg%3E"); background-repeat: no-repeat; @@ -175,14 +175,14 @@ background-size: 16px; } - /* Select 选项样式 */ + .cyberpunk-select option { background-color: rgba(11, 26, 47, 0.95); color: #fff; padding: 10px; } - /* 按钮容器 */ + .form-buttons { display: flex; justify-content: space-between; @@ -190,7 +190,7 @@ gap: 10px; } - /* 滚动条样式 */ + ::-webkit-scrollbar { width: 8px; } @@ -205,7 +205,7 @@ border-radius: 4px; } - /* 标记图标样式 */ + .marker-icon { width: 20px; height: 20px; @@ -215,7 +215,7 @@ box-shadow: 0 0 10px rgba(0, 255, 255, 0.5); } - /* 根据不同类型设置不同颜色 */ + .marker-icon.roadChange { background-color: #00FFFF; } .marker-icon.construction { background-color: #FF00FF; } .marker-icon.traffic { background-color: #FFFF00; } diff --git a/index.html b/index.html index 7d01e7a..ad18b1c 100644 --- a/index.html +++ b/index.html @@ -12,14 +12,14 @@ - +
- +
- +
@@ -44,7 +44,7 @@

Map Layers

- +

ADD NEW MARKER

@@ -93,7 +93,7 @@

ADD NEW MARKER

- + diff --git a/js/map.js b/js/map.js index 062b322..bf65557 100644 --- a/js/map.js +++ b/js/map.js @@ -1,20 +1,20 @@ -// Mapbox访问令牌 + const mapboxToken = 'pk.eyJ1IjoiZ3NodXlhIiwiYSI6ImNtMHk2bjRodTBrNmIya29obmc2OG5kZTUifQ.TM9gfDCRHU_yorUj8CQHSA'; -// 初始化地图 + mapboxgl.accessToken = mapboxToken; const map = new mapboxgl.Map({ container: 'map', style: 'mapbox://styles/mapbox/dark-v10', -center: [-122.4194, 37.7749], // 旧金山坐标 +center: [-122.4194, 37.7749], zoom: 12, minZoom: 2, maxZoom: 18 }); -// 等待地图加载 + map.on('load', () => { -// 添加交通流量图层 + map.addLayer({ 'id': 'traffic-flow', 'type': 'line', @@ -38,7 +38,7 @@ map.addLayer({ } }); -// 添加3D建筑物图层 + map.addLayer({ 'id': '3d-buildings', 'source': 'composite', @@ -63,7 +63,7 @@ map.addLayer({ } }); -// 添加道路网络图层 + map.addLayer({ 'id': 'road-network', 'type': 'line', @@ -85,7 +85,7 @@ map.addLayer({ } }); -// 添加兴趣点标签 + map.addLayer({ 'id': 'poi-labels', 'type': 'symbol', @@ -109,7 +109,7 @@ map.addLayer({ } }); -// 添加地铁线路 + map.addLayer({ 'id': 'transit-lines', 'type': 'line', @@ -126,14 +126,14 @@ map.addLayer({ }); }); -// 添加地图控件 + map.addControl(new mapboxgl.NavigationControl(), 'top-right'); map.addControl(new mapboxgl.ScaleControl({ maxWidth: 100, unit: 'metric' }), 'bottom-left'); -// 图层切换功能 + document.addEventListener('DOMContentLoaded', () => { const layers = { 'traffic-flow': 'traffic-flow-toggle', @@ -153,10 +153,10 @@ map.setLayoutProperty(layerId, 'visibility', visibility); }); }); -// 错误处理 + map.on('error', (e) => { console.error('Map error:', e.error); }); -// 导出地图实例 + window.map = map; diff --git a/js/markers.js b/js/markers.js index 809791b..59a4d81 100644 --- a/js/markers.js +++ b/js/markers.js @@ -1,7 +1,7 @@ -// 存储标记数据的数组 + let markers = []; -// 标记类型对应的颜色和图标 + const markerStyles = { roadChange: { color: '#00FFFF', icon: '🛣️' }, construction: { color: '#FF00FF', icon: '🚧' }, @@ -17,7 +17,7 @@ parking: { color: '#1E90FF', icon: '🅿️' }, charging: { color: '#32CD32', icon: '⚡' } }; -// 初始化标记功能 + function initializeMarkers() { const addMarkerBtn = document.getElementById('add-marker-btn'); const markerForm = document.getElementById('marker-form'); @@ -26,20 +26,20 @@ const descriptionField = document.getElementById('marker-description'); let selectedLocation = null; -// 点击"Add Marker"按钮显示表单 + addMarkerBtn.addEventListener('click', () => { markerForm.style.display = 'block'; descriptionField.readOnly = true; }); -// 点击地图选择位置 + map.on('click', (e) => { selectedLocation = e.lngLat; showTemporaryMarker(selectedLocation); markerForm.style.display = 'block'; descriptionField.readOnly = true; -// 添加点击动画效果 + const ripple = document.createElement('div'); ripple.className = 'map-click-ripple'; ripple.style.left = e.point.x + 'px'; @@ -48,7 +48,7 @@ document.body.appendChild(ripple); setTimeout(() => ripple.remove(), 1000); }); -// 取消按钮事件 + document.querySelector('.cancel').addEventListener('click', () => { markerForm.style.display = 'none'; removeTemporaryMarker(); @@ -57,17 +57,17 @@ addMarkerForm.reset(); descriptionField.readOnly = true; }); -// 提交表单 + addMarkerForm.addEventListener('submit', (e) => { e.preventDefault(); -// 检查是否有选择位置 + if (!selectedLocation) { showNotification('Please select a location on the map', 'error'); return; } -// 检查描述是否为空 + if (!descriptionField.value.trim()) { if (descriptionField.readOnly) { descriptionField.readOnly = false; @@ -96,34 +96,34 @@ location: selectedLocation, timestamp: new Date().toISOString() }; -// 添加标记到地图 + addMarkerToMap(newMarker); -// 保存标记数据 + markers.push(newMarker); -// 更新标记列表 + updateMarkerList(); -// 保存到本地存储 + saveMarkers(); -// 重置表单和临时标记 + addMarkerForm.reset(); markerForm.style.display = 'none'; removeTemporaryMarker(); selectedLocation = null; descriptionField.readOnly = true; -// 显示成功通知 + showNotification('Marker added successfully', 'success'); }); } -// 临时标记变量 + let temporaryMarker = null; -// 显示临时标记 + function showTemporaryMarker(location) { removeTemporaryMarker(); @@ -137,7 +137,7 @@ temporaryMarker = new mapboxgl.Marker(el) .addTo(map); } -// 移除临时标记 + function removeTemporaryMarker() { if (temporaryMarker) { temporaryMarker.remove(); @@ -145,16 +145,16 @@ temporaryMarker = null; } } -// 添加标记到地图 + function addMarkerToMap(markerData) { const style = markerStyles[markerData.type]; -// 创建标记元素 + const el = document.createElement('div'); el.className = 'marker-icon'; el.classList.add(markerData.type); -// 创建弹出窗口 + const popup = new mapboxgl.Popup({ offset: 25, closeButton: true, @@ -168,14 +168,14 @@ closeOnClick: false `); -// 添加标记到地图 + new mapboxgl.Marker(el) .setLngLat(markerData.location) .setPopup(popup) .addTo(map); } -// 更新标记列表 + function updateMarkerList() { const markerList = document.getElementById('marker-list'); markerList.innerHTML = markers.map(marker => { @@ -192,7 +192,7 @@ Duration: ${marker.duration} `; }).join(''); -// 添加点击事件 + document.querySelectorAll('.marker-item').forEach(item => { item.addEventListener('click', () => { const marker = markers.find(m => m.id === parseInt(item.dataset.id)); @@ -207,7 +207,7 @@ duration: 1000 }); } -// 加载保存的标记 + function loadSavedMarkers() { const savedMarkers = localStorage.getItem('markers'); if (savedMarkers) { @@ -217,16 +217,16 @@ updateMarkerList(); } } -// 保存标记到本地存储 + function saveMarkers() { localStorage.setItem('markers', JSON.stringify(markers)); } -// 初始化 + document.addEventListener('DOMContentLoaded', () => { initializeMarkers(); loadSavedMarkers(); }); -// 在页面关闭前保存标记 + window.addEventListener('beforeunload', saveMarkers); diff --git a/js/ui.js b/js/ui.js index 809791b..59a4d81 100644 --- a/js/ui.js +++ b/js/ui.js @@ -1,7 +1,7 @@ -// 存储标记数据的数组 + let markers = []; -// 标记类型对应的颜色和图标 + const markerStyles = { roadChange: { color: '#00FFFF', icon: '🛣️' }, construction: { color: '#FF00FF', icon: '🚧' }, @@ -17,7 +17,7 @@ parking: { color: '#1E90FF', icon: '🅿️' }, charging: { color: '#32CD32', icon: '⚡' } }; -// 初始化标记功能 + function initializeMarkers() { const addMarkerBtn = document.getElementById('add-marker-btn'); const markerForm = document.getElementById('marker-form'); @@ -26,20 +26,20 @@ const descriptionField = document.getElementById('marker-description'); let selectedLocation = null; -// 点击"Add Marker"按钮显示表单 + addMarkerBtn.addEventListener('click', () => { markerForm.style.display = 'block'; descriptionField.readOnly = true; }); -// 点击地图选择位置 + map.on('click', (e) => { selectedLocation = e.lngLat; showTemporaryMarker(selectedLocation); markerForm.style.display = 'block'; descriptionField.readOnly = true; -// 添加点击动画效果 + const ripple = document.createElement('div'); ripple.className = 'map-click-ripple'; ripple.style.left = e.point.x + 'px'; @@ -48,7 +48,7 @@ document.body.appendChild(ripple); setTimeout(() => ripple.remove(), 1000); }); -// 取消按钮事件 + document.querySelector('.cancel').addEventListener('click', () => { markerForm.style.display = 'none'; removeTemporaryMarker(); @@ -57,17 +57,17 @@ addMarkerForm.reset(); descriptionField.readOnly = true; }); -// 提交表单 + addMarkerForm.addEventListener('submit', (e) => { e.preventDefault(); -// 检查是否有选择位置 + if (!selectedLocation) { showNotification('Please select a location on the map', 'error'); return; } -// 检查描述是否为空 + if (!descriptionField.value.trim()) { if (descriptionField.readOnly) { descriptionField.readOnly = false; @@ -96,34 +96,34 @@ location: selectedLocation, timestamp: new Date().toISOString() }; -// 添加标记到地图 + addMarkerToMap(newMarker); -// 保存标记数据 + markers.push(newMarker); -// 更新标记列表 + updateMarkerList(); -// 保存到本地存储 + saveMarkers(); -// 重置表单和临时标记 + addMarkerForm.reset(); markerForm.style.display = 'none'; removeTemporaryMarker(); selectedLocation = null; descriptionField.readOnly = true; -// 显示成功通知 + showNotification('Marker added successfully', 'success'); }); } -// 临时标记变量 + let temporaryMarker = null; -// 显示临时标记 + function showTemporaryMarker(location) { removeTemporaryMarker(); @@ -137,7 +137,7 @@ temporaryMarker = new mapboxgl.Marker(el) .addTo(map); } -// 移除临时标记 + function removeTemporaryMarker() { if (temporaryMarker) { temporaryMarker.remove(); @@ -145,16 +145,16 @@ temporaryMarker = null; } } -// 添加标记到地图 + function addMarkerToMap(markerData) { const style = markerStyles[markerData.type]; -// 创建标记元素 + const el = document.createElement('div'); el.className = 'marker-icon'; el.classList.add(markerData.type); -// 创建弹出窗口 + const popup = new mapboxgl.Popup({ offset: 25, closeButton: true, @@ -168,14 +168,14 @@ closeOnClick: false `); -// 添加标记到地图 + new mapboxgl.Marker(el) .setLngLat(markerData.location) .setPopup(popup) .addTo(map); } -// 更新标记列表 + function updateMarkerList() { const markerList = document.getElementById('marker-list'); markerList.innerHTML = markers.map(marker => { @@ -192,7 +192,7 @@ Duration: ${marker.duration} `; }).join(''); -// 添加点击事件 + document.querySelectorAll('.marker-item').forEach(item => { item.addEventListener('click', () => { const marker = markers.find(m => m.id === parseInt(item.dataset.id)); @@ -207,7 +207,7 @@ duration: 1000 }); } -// 加载保存的标记 + function loadSavedMarkers() { const savedMarkers = localStorage.getItem('markers'); if (savedMarkers) { @@ -217,16 +217,16 @@ updateMarkerList(); } } -// 保存标记到本地存储 + function saveMarkers() { localStorage.setItem('markers', JSON.stringify(markers)); } -// 初始化 + document.addEventListener('DOMContentLoaded', () => { initializeMarkers(); loadSavedMarkers(); }); -// 在页面关闭前保存标记 + window.addEventListener('beforeunload', saveMarkers);