diff --git a/lib/modules/chat/controllers/chat_controller.dart b/lib/modules/chat/controllers/chat_controller.dart index 77c4e030b..f882e342f 100644 --- a/lib/modules/chat/controllers/chat_controller.dart +++ b/lib/modules/chat/controllers/chat_controller.dart @@ -450,16 +450,15 @@ class ChatController extends GetxController { try { isPlay.value = false; if (audiourl.value != musicaudio['audiourl']) { - audiourl.value = musicaudio['audiourl']; + audiourl.value = ''; await player.setUrl(musicaudio['audiourl']); + audiourl.value = musicaudio['audiourl']; player.play(); } else { player.play(); } isPlay.value = true; } catch (e) { - isPlay.value = false; - audiourl.value = ''; Logger.error('加载异常: $e'); } } diff --git a/lib/modules/chat/views/chat_navigation_card.dart b/lib/modules/chat/views/chat_navigation_card.dart new file mode 100644 index 000000000..9ea658771 --- /dev/null +++ b/lib/modules/chat/views/chat_navigation_card.dart @@ -0,0 +1,179 @@ +import 'dart:convert'; +import 'dart:math'; +import 'dart:math' as math; + +import 'package:flutter/material.dart'; +import 'package:http/http.dart' as http; + +class ChatNavigationCard extends StatefulWidget { + final Map? data; + final bool isDarkMode; + + const ChatNavigationCard(this.data, this.isDarkMode, {super.key}); + + @override + State createState() => _ChatNavigationCardState(); +} + +class _ChatNavigationCardState extends State { + final String apiKey = '8bde023b7e5e00f321ea17929f10fbae'; + LatLng startPoint = LatLng(116.397428, 39.90923); // 起点坐标 + LatLng endPoint = LatLng(116.397026, 39.903303); // 终点坐标 + + @override + void initState() { + super.initState(); + _getMapUrl(); + } + + Future _getMapUrl() async { + // 1. 获取路径数据 + final route = await _fetchRouteData(); + + // 2. 解析并简化路径 + List simplified = _simplifyPath(route, epsilon: 0.0001); + + // 3. 计算地图参数 + final zoom = _calculateAutoZoom(simplified); + + // 4. 生成静态地图URL + return _buildStaticMapUrl(simplified, zoom); + } + + Future> _fetchRouteData() async { + final url = 'https://restapi.amap.com/v3/direction/driving?' + 'origin=${startPoint.lng},${startPoint.lat}&' + 'destination=${endPoint.lng},${endPoint.lat}&' + 'key=$apiKey'; + + final response = await http.get(Uri.parse(url)); + if (response.statusCode == 200) { + final data = json.decode(response.body); + return _parsePolyline(data['route']['paths'][0]['steps']); + } + throw Exception('Failed to load route'); + } + + List _parsePolyline(List steps) { + final List path = []; + for (final step in steps) { + final points = (step['polyline'] as String).split(';'); + for (final point in points) { + final coords = point.split(',').map(double.parse).toList(); + path.add(LatLng(coords[0], coords[1])); + } + } + return path; + } + + List _simplifyPath(List points, {double epsilon = 0.0001}) { + return douglasPeucker(points, epsilon); + } + + int _calculateAutoZoom(List points, + {int mapWidth = 1024, int mapHeight = 512}) { + if (points.isEmpty) return 15; // 默认缩放级别 + + // 1. 计算坐标范围 + double minLng = double.infinity; + double maxLng = -double.infinity; + double minLat = double.infinity; + double maxLat = -double.infinity; + + for (final p in points) { + final lng = p.lng; + final lat = p.lat; + + minLng = math.min(minLng, lng); + maxLng = math.max(maxLng, lng); + minLat = math.min(minLat, lat); + maxLat = math.max(maxLat, lat); + } + + // 3. 计算缩放级别(基于Leaflet算法改进) + const worldWidth = 256; // 高德瓦片尺寸 + const worldHeight = 256; + const zoomMax = 18; // 高德最大缩放级别 + + double latRad(double lat) { + final sin = math.sin(lat * math.pi / 180); + return math.log((1 + sin) / (1 - sin)) / 2; + } + + final latFraction = (latRad(maxLat) - latRad(minLat)) / math.pi; + final lngDiff = maxLng - minLng; + final lngFraction = ((lngDiff < 0) ? (lngDiff + 360) : lngDiff) / 360; + + final latZoom = + math.log((mapHeight / worldHeight) / latFraction) / math.ln2; + final lngZoom = math.log((mapWidth / worldWidth) / lngFraction) / math.ln2; + + return math.min(math.min(latZoom, lngZoom).floor(), zoomMax); + } + + String _buildStaticMapUrl(List path, int zoom) { + final pathParam = path.map((p) => '${p.lng},${p.lat}').join(';'); + return 'https://restapi.amap.com/v3/staticmap?' + 'zoom=$zoom&' + 'size=1024*512&' + 'paths=10,0x0000ff,1,,:$pathParam&' + 'markers=mid,,A:${startPoint.lng},${startPoint.lat}|' + 'mid,,B:${endPoint.lng},${endPoint.lat}&' + 'key=$apiKey'; + } + + List douglasPeucker(List points, double epsilon) { + if (points.length < 3) return points; + + double maxDistance = 0; + int index = 0; + + for (int i = 1; i < points.length - 1; i++) { + double d = _perpendicularDistance(points[i], points.first, points.last); + if (d > maxDistance) { + index = i; + maxDistance = d; + } + } + + if (maxDistance > epsilon) { + return [ + ...douglasPeucker(points.sublist(0, index + 1), epsilon), + ...douglasPeucker(points.sublist(index), epsilon).skip(1) + ]; + } + return [points.first, points.last]; + } + + double _perpendicularDistance(LatLng point, LatLng start, LatLng end) { + final area = (end.lng - start.lng) * (point.lat - start.lat) - + (end.lat - start.lat) * (point.lng - start.lng); + final length = + sqrt(pow(end.lng - start.lng, 2) + pow(end.lat - start.lat, 2)); + return area.abs() / length; + } + + @override + Widget build(BuildContext context) { + return Column( + children: [ + FutureBuilder( + future: _getMapUrl(), + builder: (BuildContext context, AsyncSnapshot snapshot) { + if (snapshot.hasData) { + return Image.network(snapshot.data); + } + return const CircularProgressIndicator(); + }, + ), + ], + ); + } +} + +class LatLng { + final double lat; + final double lng; + + LatLng(this.lng, this.lat); // 高德使用经度在前纬度在后的格式 +} diff --git a/lib/modules/chat/views/chat_view.dart b/lib/modules/chat/views/chat_view.dart index 2c6adee7b..073d079dc 100644 --- a/lib/modules/chat/views/chat_view.dart +++ b/lib/modules/chat/views/chat_view.dart @@ -6,6 +6,7 @@ import '../controllers/chat_controller.dart'; import '../models/message_model.dart'; import '../../../core/theme/app_colors.dart'; import 'chat_music_card.dart'; +import 'chat_navigation_card.dart'; class ChatView extends GetView { const ChatView({Key? key}) : super(key: key); @@ -354,8 +355,11 @@ class ChatView extends GetView { ); } if (message.data != null) { + // if (message.data?['musicaudio'] != null) { + // box = ChatMusicCard(message.data, isDarkMode); + // } if (message.data?['musicaudio'] != null) { - box = ChatMusicCard(message.data, isDarkMode); + box = ChatNavigationCard(message.data, isDarkMode); } } return box;