Új térkép widget, melyet több oldal is használ
This commit is contained in:
@@ -0,0 +1,319 @@
|
||||
// lib/widgets/coordinate_panel.dart
|
||||
|
||||
import 'dart:math';
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:intl/intl.dart';
|
||||
|
||||
import '../services/gnss/gnss_service.dart';
|
||||
|
||||
class CoordinatePanel extends StatelessWidget {
|
||||
final RxDouble eovY;
|
||||
final RxDouble eovX;
|
||||
final RxDouble horError;
|
||||
final RxDouble vertError;
|
||||
final RxDouble? altitudeMsl;
|
||||
final RxDouble? geoidSeparation;
|
||||
final RxBool? ntripConnected;
|
||||
final RxInt? ntripBytes;
|
||||
final RxInt? ntripPackets;
|
||||
final RxInt? ggaPackets;
|
||||
|
||||
const CoordinatePanel({
|
||||
super.key,
|
||||
required this.eovY,
|
||||
required this.eovX,
|
||||
required this.horError,
|
||||
required this.vertError,
|
||||
this.altitudeMsl,
|
||||
this.geoidSeparation,
|
||||
this.ntripConnected,
|
||||
this.ntripBytes,
|
||||
this.ntripPackets,
|
||||
this.ggaPackets,
|
||||
});
|
||||
|
||||
/// Gyors factory — MapSurveyController mezőiből.
|
||||
factory CoordinatePanel.fromController(dynamic ctrl) {
|
||||
final y = RxDouble(0.0);
|
||||
final x = RxDouble(0.0);
|
||||
// Figyeli az eov változást és frissíti Y/X-et
|
||||
ctrl.eov.listen((eov) {
|
||||
y.value = (eov.Y as num).toDouble();
|
||||
x.value = (eov.X as num).toDouble();
|
||||
});
|
||||
return CoordinatePanel(
|
||||
eovY: y,
|
||||
eovX: x,
|
||||
horError: ctrl.gpsLatitudeError as RxDouble,
|
||||
vertError: ctrl.gpsAltitudeError as RxDouble,
|
||||
altitudeMsl: ctrl.gpsAltitude as RxDouble,
|
||||
geoidSeparation: ctrl.gpsGeoidSeparation as RxDouble,
|
||||
ntripConnected: ctrl.ntripIsConnected as RxBool,
|
||||
ntripBytes: ctrl.ntripReceivedData as RxInt,
|
||||
ntripPackets: ctrl.ntripDataPacketNumbers as RxInt,
|
||||
ggaPackets: ctrl.ggaSenDataPacketNumber as RxInt,
|
||||
);
|
||||
}
|
||||
|
||||
static final _fmtEov = NumberFormat('###,###,##0.000', 'hu-HU');
|
||||
static final _fmtEovZ = NumberFormat('###,##0.000', 'hu-HU');
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final gnss = GnssService.to;
|
||||
|
||||
return Obx(() {
|
||||
final quality = gnss.gpsQuality.value;
|
||||
final hasData = quality > 0;
|
||||
final color = _qualityColor(quality);
|
||||
|
||||
return AnimatedContainer(
|
||||
duration: const Duration(milliseconds: 300),
|
||||
decoration: BoxDecoration(
|
||||
color: Colors.black.withOpacity(0.78),
|
||||
borderRadius: BorderRadius.circular(10),
|
||||
border: Border.all(color: color.withOpacity(0.6), width: 1.5),
|
||||
),
|
||||
child: Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
// ── Fejléc ──────────────────────────────────────────
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 8, 10, 6),
|
||||
child: Row(children: [
|
||||
// Fix chip
|
||||
Container(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 8, vertical: 3),
|
||||
decoration: BoxDecoration(
|
||||
color: color.withOpacity(0.2),
|
||||
borderRadius: BorderRadius.circular(5),
|
||||
border: Border.all(color: color.withOpacity(0.5)),
|
||||
),
|
||||
child: Row(mainAxisSize: MainAxisSize.min, children: [
|
||||
Container(
|
||||
width: 7,
|
||||
height: 7,
|
||||
decoration:
|
||||
BoxDecoration(color: color, shape: BoxShape.circle),
|
||||
),
|
||||
const SizedBox(width: 5),
|
||||
Text(_qualityLabel(quality),
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w700,
|
||||
)),
|
||||
]),
|
||||
),
|
||||
const Spacer(),
|
||||
// Műholdak
|
||||
if (hasData) ...[
|
||||
Icon(Icons.satellite_alt,
|
||||
size: 11, color: Colors.white.withOpacity(0.5)),
|
||||
const SizedBox(width: 3),
|
||||
Text('${gnss.satelliteCount.value}',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.55), fontSize: 11)),
|
||||
const SizedBox(width: 10),
|
||||
],
|
||||
// UTC
|
||||
Icon(Icons.access_time,
|
||||
size: 11, color: Colors.white.withOpacity(0.4)),
|
||||
const SizedBox(width: 3),
|
||||
Text(
|
||||
gnss.utcFix.value.isNotEmpty
|
||||
? '${gnss.utcFix.value} UTC'
|
||||
: '--:--:-- UTC',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.45), fontSize: 11)),
|
||||
]),
|
||||
),
|
||||
|
||||
if (hasData) ...[
|
||||
_Div(),
|
||||
|
||||
// ── EOV koordináták ──────────────────────────────
|
||||
Padding(
|
||||
padding:
|
||||
const EdgeInsets.symmetric(horizontal: 10, vertical: 4),
|
||||
child: Obx(() {
|
||||
final alt = altitudeMsl?.value ?? gnss.altitude.value;
|
||||
final geoSep =
|
||||
geoidSeparation?.value ?? gnss.geoidSeparation.value;
|
||||
return Column(children: [
|
||||
_Row('EOV Y', _fmtEov.format(eovY.value), 'm', bold: true),
|
||||
const SizedBox(height: 3),
|
||||
_Row('EOV X', _fmtEov.format(eovX.value), 'm', bold: true),
|
||||
const SizedBox(height: 6),
|
||||
_Row('H (MSL)', _fmtEovZ.format(alt), 'm'),
|
||||
const SizedBox(height: 2),
|
||||
_Row('h (WGS84)', _fmtEovZ.format(alt + geoSep), 'm',
|
||||
dimmed: true),
|
||||
]);
|
||||
}),
|
||||
),
|
||||
|
||||
_Div(),
|
||||
|
||||
// ── Pontosság ────────────────────────────────────
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 10, 6),
|
||||
child: Obx(() {
|
||||
final hor = max(horError.value, 0.0);
|
||||
final vert = vertError.value;
|
||||
final geo =
|
||||
geoidSeparation?.value ?? gnss.geoidSeparation.value;
|
||||
return Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
_Chip(Icons.open_in_full, 'Vízszintes',
|
||||
'±${hor.toStringAsFixed(3)} m', _errorColor(hor)),
|
||||
_Chip(Icons.height, 'Függőleges',
|
||||
'±${vert.toStringAsFixed(3)} m', _errorColor(vert)),
|
||||
_Chip(Icons.water, 'Geoid N',
|
||||
'${geo.toStringAsFixed(2)} m', Colors.white54),
|
||||
],
|
||||
);
|
||||
}),
|
||||
),
|
||||
|
||||
// ── NTRIP ────────────────────────────────────────
|
||||
if (ntripConnected != null)
|
||||
Obx(
|
||||
() => ntripConnected!.value
|
||||
? Column(children: [
|
||||
_Div(),
|
||||
Padding(
|
||||
padding: const EdgeInsets.fromLTRB(10, 4, 10, 6),
|
||||
child: Row(children: [
|
||||
Container(
|
||||
width: 7,
|
||||
height: 7,
|
||||
decoration: const BoxDecoration(
|
||||
color: Colors.greenAccent,
|
||||
shape: BoxShape.circle),
|
||||
),
|
||||
const SizedBox(width: 6),
|
||||
const Text('NTRIP',
|
||||
style: TextStyle(
|
||||
color: Colors.greenAccent,
|
||||
fontSize: 11,
|
||||
fontWeight: FontWeight.w600,
|
||||
)),
|
||||
const Spacer(),
|
||||
Text(
|
||||
'${ntripBytes?.value ?? 0} byte'
|
||||
' · ${ntripPackets?.value ?? 0} cs.',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.5),
|
||||
fontSize: 10,
|
||||
)),
|
||||
const SizedBox(width: 8),
|
||||
Text('GGA: ${ggaPackets?.value ?? 0}',
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.4),
|
||||
fontSize: 10,
|
||||
)),
|
||||
]),
|
||||
),
|
||||
])
|
||||
: const SizedBox.shrink(),
|
||||
),
|
||||
],
|
||||
],
|
||||
),
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// ── Mini widgetek ─────────────────────────────────────────────────────
|
||||
|
||||
class _Div extends StatelessWidget {
|
||||
@override
|
||||
Widget build(BuildContext context) =>
|
||||
Container(height: 1, color: Colors.white.withOpacity(0.08));
|
||||
}
|
||||
|
||||
class _Row extends StatelessWidget {
|
||||
final String label, value, unit;
|
||||
final bool bold, dimmed;
|
||||
const _Row(this.label, this.value, this.unit,
|
||||
{this.bold = false, this.dimmed = false});
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(label,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.5), fontSize: 11)),
|
||||
Row(children: [
|
||||
Text(value,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(dimmed ? 0.45 : 1.0),
|
||||
fontSize: bold ? 15 : 13,
|
||||
fontWeight: bold ? FontWeight.w600 : FontWeight.w400,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
)),
|
||||
const SizedBox(width: 3),
|
||||
Text(unit,
|
||||
style: TextStyle(
|
||||
color: Colors.white.withOpacity(0.35), fontSize: 10)),
|
||||
]),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
class _Chip extends StatelessWidget {
|
||||
final IconData icon;
|
||||
final String label, value;
|
||||
final Color color;
|
||||
const _Chip(this.icon, this.label, this.value, this.color);
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) => Column(
|
||||
mainAxisSize: MainAxisSize.min,
|
||||
children: [
|
||||
Icon(icon, size: 12, color: Colors.white.withOpacity(0.4)),
|
||||
const SizedBox(height: 2),
|
||||
Text(value,
|
||||
style: TextStyle(
|
||||
color: color,
|
||||
fontSize: 12,
|
||||
fontWeight: FontWeight.w600,
|
||||
fontFeatures: const [FontFeature.tabularFigures()],
|
||||
)),
|
||||
Text(label,
|
||||
style:
|
||||
TextStyle(color: Colors.white.withOpacity(0.4), fontSize: 9)),
|
||||
],
|
||||
);
|
||||
}
|
||||
|
||||
// ── Segédfüggvények ───────────────────────────────────────────────────
|
||||
|
||||
Color _qualityColor(int q) => switch (q) {
|
||||
4 => Colors.greenAccent,
|
||||
5 => Colors.lightGreen,
|
||||
2 => Colors.blue,
|
||||
1 => Colors.orange,
|
||||
_ => Colors.red,
|
||||
};
|
||||
|
||||
String _qualityLabel(int q) => switch (q) {
|
||||
4 => 'RTK Fix',
|
||||
5 => 'RTK Float',
|
||||
2 => 'DGPS',
|
||||
1 => 'GPS',
|
||||
_ => 'Nincs fix',
|
||||
};
|
||||
|
||||
Color _errorColor(double e) {
|
||||
if (e < 0.05) return Colors.greenAccent;
|
||||
if (e < 0.2) return Colors.green;
|
||||
if (e < 1.0) return Colors.orange;
|
||||
return Colors.red;
|
||||
}
|
||||
Reference in New Issue
Block a user