manager-app/lib/Components/map_geometry_picker.dart

362 lines
13 KiB
Dart

import 'dart:math' as math;
import 'package:flutter/material.dart';
import 'package:flutter_map/flutter_map.dart';
import 'package:latlong2/latlong.dart';
import 'package:manager_api_new/api.dart';
import 'package:manager_app/constants.dart';
import 'package:manager_app/Components/rounded_button.dart';
import 'package:manager_app/Components/color_picker.dart';
class MapGeometryPicker extends StatefulWidget {
final GeometryDTO? initialGeometry;
final String? initialColor;
final Function(GeometryDTO, String) onSave;
MapGeometryPicker({
this.initialGeometry,
this.initialColor,
required this.onSave,
});
@override
_MapGeometryPickerState createState() => _MapGeometryPickerState();
}
class _MapGeometryPickerState extends State<MapGeometryPicker> {
List<LatLng> points = [];
String currentType = "Point";
Color selectedColor = kPrimaryColor;
final MapController _mapController = MapController();
@override
void initState() {
super.initState();
if (widget.initialGeometry != null) {
currentType = widget.initialGeometry!.type ?? "Point";
_parseInitialGeometry();
}
if (widget.initialColor != null) {
try {
selectedColor = _hexToColor(widget.initialColor!);
} catch (e) {
selectedColor = kPrimaryColor;
}
}
}
Color _hexToColor(String hex) {
hex = hex.replaceFirst('#', '');
if (hex.length == 6) hex = 'FF' + hex;
return Color(int.parse(hex, radix: 16));
}
void _parseInitialGeometry() {
if (widget.initialGeometry?.coordinates == null) return;
try {
if (currentType == "Point") {
var coords = widget.initialGeometry!.coordinates as List<dynamic>;
points = [LatLng(coords[0].toDouble(), coords[1].toDouble())];
} else if (currentType == "LineString" || currentType == "Polygon") {
var list = widget.initialGeometry!.coordinates as List<dynamic>;
points = list.map((e) {
var pair = e as List<dynamic>;
return LatLng(pair[0].toDouble(), pair[1].toDouble());
}).toList();
}
} catch (e) {
print("Error parsing geometry: $e");
}
}
void _handleTap(TapPosition tapPosition, LatLng latLng) {
setState(() {
if (currentType == "Point") {
points = [latLng];
} else {
points.add(latLng);
}
});
}
GeometryDTO _buildGeometry() {
if (currentType == "Point") {
return GeometryDTO(
type: "Point",
coordinates: points.isNotEmpty
? [points[0].latitude, points[0].longitude]
: null,
);
} else {
return GeometryDTO(
type: currentType,
coordinates: points.map((e) => [e.latitude, e.longitude]).toList(),
);
}
}
String _colorToHex(Color color) {
return '#${color.value.toRadixString(16).padLeft(8, '0').substring(2)}';
}
@override
Widget build(BuildContext context) {
Size size = MediaQuery.of(context).size;
return Dialog(
insetPadding: EdgeInsets.all(20),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(15)),
child: Container(
width: size.width * 0.9,
height: size.height * 0.9,
child: ClipRRect(
borderRadius: BorderRadius.circular(15),
child: Column(
children: [
Container(
padding: EdgeInsets.symmetric(horizontal: 16, vertical: 12),
color: kPrimaryColor,
child: Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Text("Éditeur de Géométrie",
style: TextStyle(
color: Colors.white,
fontSize: 18,
fontWeight: FontWeight.bold)),
Row(
children: [
IconButton(
icon: Icon(Icons.palette, color: Colors.white),
onPressed: () {
showColorPicker(selectedColor, (Color color) {
setState(() => selectedColor = color);
}, context);
},
),
IconButton(
icon: Icon(Icons.delete, color: Colors.white),
onPressed: () => setState(() => points.clear()),
),
IconButton(
icon: Icon(Icons.close, color: Colors.white),
onPressed: () => Navigator.pop(context),
),
],
),
],
),
),
Padding(
padding: const EdgeInsets.all(12.0),
child: Column(
children: [
Wrap(
spacing: 12,
alignment: WrapAlignment.center,
children: [
_buildTypeButton("Point", Icons.location_on),
_buildTypeButton("LineString", Icons.show_chart),
_buildTypeButton("Polygon", Icons.pentagon),
],
),
SizedBox(height: 8),
Text(
_getInstructions(),
style: TextStyle(
fontStyle: FontStyle.italic, color: Colors.grey[600]),
textAlign: TextAlign.center,
),
],
),
),
Expanded(
child: Stack(
children: [
FlutterMap(
key: _mapKey,
mapController: _mapController,
options: MapOptions(
initialCenter: points.isNotEmpty
? points[0]
: LatLng(50.429333, 4.891434),
initialZoom: 14,
onTap: _handleTap,
),
children: [
TileLayer(
urlTemplate:
"https://tile.openstreetmap.org/{z}/{x}/{y}.png",
),
if (currentType == "Polygon" && points.length >= 3)
PolygonLayer(
polygons: [
Polygon(
points: points,
color: selectedColor.withOpacity(0.3),
borderStrokeWidth: 2,
borderColor: selectedColor,
isFilled: true,
),
],
),
if (currentType == "LineString" && points.length >= 2)
PolylineLayer(
polylines: [
Polyline(
points: points,
color: selectedColor,
strokeWidth: 4,
),
],
),
MarkerLayer(
markers: points.asMap().entries.map((entry) {
int idx = entry.key;
LatLng p = entry.value;
return Marker(
point: p,
width: 30,
height: 30,
child: GestureDetector(
onPanUpdate: (details) {
_handleDrag(idx, details);
},
child: Container(
decoration: BoxDecoration(
color: Colors.white,
shape: BoxShape.circle,
border: Border.all(
color: selectedColor, width: 2),
boxShadow: [
BoxShadow(
blurRadius: 4,
color: Colors.black26,
offset: Offset(0, 2))
],
),
child: Center(
child: Icon(Icons.circle,
color: selectedColor, size: 14),
),
),
),
);
}).toList(),
),
],
),
],
),
),
Container(
padding: EdgeInsets.all(16),
decoration: BoxDecoration(
color: Colors.white,
boxShadow: [
BoxShadow(
color: Colors.black12,
blurRadius: 4,
offset: Offset(0, -2))
],
),
child: Row(
mainAxisAlignment: MainAxisAlignment.end,
children: [
Container(
height: 45,
child: RoundedButton(
text: "Annuler",
color: Colors.grey[200]!,
textColor: Colors.black87,
press: () => Navigator.pop(context),
fontSize: 16,
horizontal: 24,
),
),
SizedBox(width: 12),
Container(
height: 45,
child: RoundedButton(
text: "Sauvegarder",
color: kPrimaryColor,
press: () {
widget.onSave(
_buildGeometry(), _colorToHex(selectedColor));
Navigator.pop(context);
},
fontSize: 16,
horizontal: 32,
),
),
],
),
)
],
),
),
),
);
}
String _getInstructions() {
switch (currentType) {
case "Point":
return "Touchez la carte pour placer le point. Faites glisser le point pour le déplacer.";
case "LineString":
return "Touchez la carte pour ajouter des points à la ligne. Faites glisser un point pour le déplacer.";
case "Polygon":
return "Touchez la carte pour définir les sommets du polygone. Faites glisser un sommet pour le déplacer.";
default:
return "";
}
}
final GlobalKey _mapKey = GlobalKey();
void _handleDrag(int index, DragUpdateDetails details) {
if (index < 0 || index >= points.length) return;
final RenderBox? mapBox =
_mapKey.currentContext?.findRenderObject() as RenderBox?;
if (mapBox != null) {
final Offset localOffset = mapBox.globalToLocal(details.globalPosition);
try {
LatLng newLatLng = _mapController.camera
.pointToLatLng(math.Point(localOffset.dx, localOffset.dy));
setState(() {
points[index] = newLatLng;
});
} catch (e) {
print("Error dragging point: $e");
}
}
}
Widget _buildTypeButton(String type, IconData icon) {
bool isSelected = currentType == type;
return ChoiceChip(
label: Text(type == "LineString"
? "Ligne"
: type == "Polygon"
? "Polygone"
: "Point"),
avatar: Icon(icon,
size: 18, color: isSelected ? Colors.white : kPrimaryColor),
selected: isSelected,
onSelected: (val) {
if (val) {
setState(() {
currentType = type;
if (currentType == "Point" && points.length > 1) {
points = [points[0]];
}
});
}
},
selectedColor: kPrimaryColor,
labelStyle: TextStyle(color: isSelected ? Colors.white : kPrimaryColor),
shape: RoundedRectangleBorder(borderRadius: BorderRadius.circular(20)),
);
}
}