init
This commit is contained in:
commit
e833bcaec4
224 files changed
+11812
No files matched your search
@@ -0,0 +1,670 @@
|
||||
// widgets/vigna_map_widget.dart
|
||||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_map/flutter_map.dart';
|
||||
import 'package:get/get.dart';
|
||||
import 'package:get/get_core/src/get_main.dart';
|
||||
import 'package:latlong2/latlong.dart';
|
||||
import 'package:u_vineyard/entities/coordinates.dart';
|
||||
import 'package:u_vineyard/entities/vineyard.dart';
|
||||
import 'package:u_vineyard/template/src/providers/auth.provider.dart';
|
||||
import 'package:u_vineyard/template/src/widgets/card.widget.dart';
|
||||
|
||||
// Definizione del tipo di callback per i tap sulla mappa
|
||||
typedef MapTapCallback = void Function(TapPosition tapPosition, LatLng latLng);
|
||||
|
||||
class MainMapWidget extends StatefulWidget {
|
||||
const MainMapWidget({super.key});
|
||||
|
||||
@override
|
||||
State<MainMapWidget> createState() => _MainMapWidgetState();
|
||||
}
|
||||
|
||||
class _MainMapWidgetState extends State<MainMapWidget> {
|
||||
final MapController _mapController = MapController();
|
||||
final Map<String, String> _mapUrlTemplates = {
|
||||
'Standard': 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
'Terreno (OpenTopoMap)': 'https://opentopomap.org/{z}/{x}/{y}.png',
|
||||
'Satellite (ESRI)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||
};
|
||||
|
||||
final Map<String, Widget> _mapAttributions = {
|
||||
'Standard': const Text('© OpenStreetMap contributors'),
|
||||
'Terreno (OpenTopoMap)': const Text('Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)'),
|
||||
'Satellite (ESRI)': const Text('Tiles © Esri — Source: Esri, i-cubed, USDA FSA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'),
|
||||
};
|
||||
|
||||
String? _selectedMapType = 'Standard';
|
||||
double _currentZoom = 13.0; // Questo zoom verrà sovrascritto se ci sono aree
|
||||
|
||||
// Variabile per tenere traccia se la mappa è già stata centrata una volta
|
||||
bool _mapCenteredInitially = false;
|
||||
|
||||
@override
|
||||
void initState() {
|
||||
super.initState();
|
||||
// Non inizializzare qui _vineyardList o _areas
|
||||
}
|
||||
|
||||
void _zoomIn() {
|
||||
setState(() {
|
||||
_currentZoom += 1;
|
||||
_mapController.move(_mapController.camera.center, _currentZoom);
|
||||
});
|
||||
}
|
||||
|
||||
void _zoomOut() {
|
||||
setState(() {
|
||||
_currentZoom -= 1;
|
||||
_mapController.move(_mapController.camera.center, _currentZoom);
|
||||
});
|
||||
}
|
||||
|
||||
// Funzione per calcolare i limiti e centrare la mappa
|
||||
void _fitMapToPolygons(List<Map<int, Coordinates>> areas) {
|
||||
if (areas.isEmpty) {
|
||||
// Se non ci sono aree, centra sulla posizione predefinita
|
||||
_mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Raccogli tutti i punti da tutte le aree
|
||||
List<LatLng> allPoints = [];
|
||||
for (var areaMap in areas) {
|
||||
for (var coord in areaMap.values) {
|
||||
allPoints.add(LatLng(coord.latitude, coord.longitude));
|
||||
}
|
||||
}
|
||||
|
||||
if (allPoints.isEmpty) {
|
||||
// Se per qualche motivo non ci sono punti validi, torna al default
|
||||
_mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
||||
return;
|
||||
}
|
||||
|
||||
// Calcola i limiti (min/max latitudine e longitudine)
|
||||
double? minLat, maxLat, minLon, maxLon;
|
||||
|
||||
for (var point in allPoints) {
|
||||
if (minLat == null || point.latitude < minLat) minLat = point.latitude;
|
||||
if (maxLat == null || point.latitude > maxLat) maxLat = point.latitude;
|
||||
if (minLon == null || point.longitude < minLon) minLon = point.longitude;
|
||||
if (maxLon == null || point.longitude > maxLon) maxLon = point.longitude;
|
||||
}
|
||||
|
||||
if (minLat != null && maxLat != null && minLon != null && maxLon != null) {
|
||||
// Crea un LatLngBounds
|
||||
final LatLngBounds bounds = LatLngBounds(
|
||||
LatLng(minLat, minLon),
|
||||
LatLng(maxLat, maxLon),
|
||||
);
|
||||
|
||||
// Adatta la camera per mostrare tutti i punti
|
||||
_mapController.fitCamera(
|
||||
CameraFit.bounds(
|
||||
bounds: bounds,
|
||||
padding: const EdgeInsets.all(50.0), // Aggiungi un po' di padding
|
||||
),
|
||||
);
|
||||
// Aggiorna lo zoom corrente dopo il fit
|
||||
_currentZoom = _mapController.camera.zoom;
|
||||
}
|
||||
}
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
final AuthProvider authProvider = Get.find<AuthProvider>();
|
||||
|
||||
return CardWidget(
|
||||
height: 700,
|
||||
child: Obx(() {
|
||||
final List<Vineyard> vineyardList = authProvider.profile?.owners?.expand((owner) => owner.vineyards ?? <Vineyard>[]).cast<Vineyard>().toList() ?? [];
|
||||
final List<Map<int, Coordinates>> areas = vineyardList
|
||||
.map((vineyard) => vineyard.area)
|
||||
.whereType<Map<int, Coordinates>>()
|
||||
.toList();
|
||||
|
||||
// Centra la mappa solo una volta o quando le aree cambiano significativamente
|
||||
// Per semplicità, la centreremo solo la prima volta che le aree vengono caricate
|
||||
// o se il flag _mapCenteredInitially è falso e ci sono aree.
|
||||
if (areas.isNotEmpty && !_mapCenteredInitially) {
|
||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||
_fitMapToPolygons(areas);
|
||||
_mapCenteredInitially = true; // Imposta il flag a true dopo il primo centraggio
|
||||
});
|
||||
} else if (areas.isEmpty && _mapCenteredInitially) {
|
||||
// Se le aree diventano vuote dopo essere state caricate, resetta il flag
|
||||
_mapCenteredInitially = false;
|
||||
}
|
||||
|
||||
|
||||
// Logica per initialCenter della mappa (usata solo alla prima costruzione)
|
||||
// Dopo il primo render, _mapController.move gestirà il centro e lo zoom
|
||||
LatLng initialMapCenter;
|
||||
if (vineyardList.isNotEmpty && vineyardList.first.coordinates != null) {
|
||||
initialMapCenter = LatLng(vineyardList.first.coordinates!.latitude, vineyardList.first.coordinates!.longitude);
|
||||
} else {
|
||||
initialMapCenter = const LatLng(45.4642, 9.1900); // Centro di default (Milano)
|
||||
}
|
||||
|
||||
return Column(
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Vigne',
|
||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.green[800]),
|
||||
),
|
||||
DropdownButton<String>(
|
||||
value: _selectedMapType,
|
||||
items: const [
|
||||
DropdownMenuItem(value: 'Standard', child: Text('Standard')),
|
||||
DropdownMenuItem(value: 'Terreno (OpenTopoMap)', child: Text('Terreno')),
|
||||
DropdownMenuItem(value: 'Satellite (ESRI)', child: Text('Satellite')),
|
||||
],
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
_selectedMapType = newValue;
|
||||
});
|
||||
},
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 16),
|
||||
Expanded(
|
||||
child: Container(
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: FlutterMap(
|
||||
mapController: _mapController,
|
||||
options: MapOptions(
|
||||
initialCenter: initialMapCenter,
|
||||
initialZoom: _currentZoom, // Usa _currentZoom che può essere aggiornato da _fitMapToPolygons
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: _mapUrlTemplates[_selectedMapType]!,
|
||||
userAgentPackageName: 'com.example.vigna_app',
|
||||
),
|
||||
RichAttributionWidget(
|
||||
attributions: [
|
||||
TextSourceAttribution(
|
||||
(_mapAttributions[_selectedMapType]! as Text).data!,
|
||||
onTap: () => print('Attribution tapped: ${(_mapAttributions[_selectedMapType]! as Text).data}'),
|
||||
),
|
||||
],
|
||||
),
|
||||
MarkerLayer(
|
||||
markers: vineyardList.map((vineyard) {
|
||||
return Marker(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
point: vineyard.coordinates != null
|
||||
? LatLng(vineyard.coordinates!.latitude, vineyard.coordinates!.longitude)
|
||||
: const LatLng(0, 0),
|
||||
child: Tooltip(
|
||||
message: vineyard.name,
|
||||
child: const Icon(Icons.grass, color: Colors.green, size: 30.0),
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
Positioned(
|
||||
top: 40,
|
||||
right: 10,
|
||||
child: Column(
|
||||
children: [
|
||||
FloatingActionButton(
|
||||
heroTag: 'zoomIn',
|
||||
mini: true,
|
||||
onPressed: _zoomIn,
|
||||
child: const Icon(Icons.add),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
FloatingActionButton(
|
||||
heroTag: 'zoomOut',
|
||||
mini: true,
|
||||
onPressed: _zoomOut,
|
||||
child: const Icon(Icons.remove),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
if (areas.isNotEmpty)
|
||||
PolygonLayer(
|
||||
polygons: areas.expand((areaMap) {
|
||||
final List<LatLng> polygonPoints = areaMap.values
|
||||
.map((coord) => LatLng(coord.latitude, coord.longitude))
|
||||
.toList();
|
||||
|
||||
if (polygonPoints.isNotEmpty && polygonPoints.first != polygonPoints.last) {
|
||||
polygonPoints.add(polygonPoints.first);
|
||||
}
|
||||
|
||||
return [
|
||||
if (polygonPoints.length >= 3)
|
||||
Polygon(
|
||||
points: polygonPoints,
|
||||
color: Colors.blue.withOpacity(0.3),
|
||||
borderStrokeWidth: 2,
|
||||
borderColor: Colors.blue[900]!,
|
||||
),
|
||||
];
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
);
|
||||
}),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// class MainMapWidget extends StatefulWidget {
|
||||
// const MainMapWidget({super.key});
|
||||
|
||||
// @override
|
||||
// State<MainMapWidget> createState() => _MainMapWidgetState();
|
||||
// }
|
||||
|
||||
// class _MainMapWidgetState extends State<MainMapWidget> {
|
||||
// // Le variabili di stato devono essere qui
|
||||
// final MapController _mapController = MapController();
|
||||
// final Map<String, String> _mapUrlTemplates = {
|
||||
// 'Standard': 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
// 'Terreno (OpenTopoMap)': 'https://opentopomap.org/{z}/{x}/{y}.png',
|
||||
// 'Satellite (ESRI)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||
// };
|
||||
|
||||
// final Map<String, Widget> _mapAttributions = {
|
||||
// 'Standard': const Text('© OpenStreetMap contributors'),
|
||||
// 'Terreno (OpenTopoMap)': const Text('Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)'),
|
||||
// 'Satellite (ESRI)': const Text('Tiles © Esri — Source: Esri, i-cubed, USDA FSA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'),
|
||||
// };
|
||||
|
||||
// String? _selectedMapType = 'Standard'; // Tipo di mappa selezionato
|
||||
// double _currentZoom = 13.0;
|
||||
// List<Vineyard> _vineyardList = [];
|
||||
// List<Map<int, Coordinates>> _areas = [];
|
||||
|
||||
// @override
|
||||
// void initState() {
|
||||
// super.initState();
|
||||
// _vineyardList = Get.find<AuthProvider>().profile?.owners?.expand((owner) => owner.vineyards ?? <Vineyard>[]).cast<Vineyard>().toList() ?? [];
|
||||
// _areas = _vineyardList
|
||||
// .map((vineyard) => vineyard.area)
|
||||
// .whereType<Map<int, Coordinates>>()
|
||||
// .toList();
|
||||
// }
|
||||
|
||||
// void _zoomIn() {
|
||||
// setState(() {
|
||||
// _currentZoom += 1;
|
||||
// _mapController.move(_mapController.camera.center, _currentZoom);
|
||||
// });
|
||||
// }
|
||||
|
||||
// void _zoomOut() {
|
||||
// setState(() {
|
||||
// _currentZoom -= 1;
|
||||
// _mapController.move(_mapController.camera.center, _currentZoom);
|
||||
// });
|
||||
|
||||
// void _fitMapToPolygons(List<Map<int, Coordinates>> areas) {
|
||||
// if (areas.isEmpty) {
|
||||
// // Se non ci sono aree, centra sulla posizione predefinita
|
||||
// _mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Raccogli tutti i punti da tutte le aree
|
||||
// List<LatLng> allPoints = [];
|
||||
// for (var areaMap in areas) {
|
||||
// for (var coord in areaMap.values) {
|
||||
// allPoints.add(LatLng(coord.latitude, coord.longitude));
|
||||
// }
|
||||
// }
|
||||
|
||||
// if (allPoints.isEmpty) {
|
||||
// // Se per qualche motivo non ci sono punti validi, torna al default
|
||||
// _mapController.move(const LatLng(45.4642, 9.1900), 10.0);
|
||||
// return;
|
||||
// }
|
||||
|
||||
// // Calcola i limiti (min/max latitudine e longitudine)
|
||||
// double? minLat, maxLat, minLon, maxLon;
|
||||
|
||||
// for (var point in allPoints) {
|
||||
// if (minLat == null || point.latitude < minLat) minLat = point.latitude;
|
||||
// if (maxLat == null || point.latitude > maxLat) maxLat = point.latitude;
|
||||
// if (minLon == null || point.longitude < minLon) minLon = point.longitude;
|
||||
// if (maxLon == null || point.longitude > maxLon) maxLon = point.longitude;
|
||||
// }
|
||||
|
||||
// if (minLat != null && maxLat != null && minLon != null && maxLon != null) {
|
||||
// // Crea un LatLngBounds
|
||||
// final LatLngBounds bounds = LatLngBounds(
|
||||
// LatLng(minLat, minLon),
|
||||
// LatLng(maxLat, maxLon),
|
||||
// );
|
||||
|
||||
// // Adatta la camera per mostrare tutti i punti
|
||||
// _mapController.fitCamera(
|
||||
// CameraFit.bounds(
|
||||
// bounds: bounds,
|
||||
// padding: const EdgeInsets.all(50.0), // Aggiungi un po' di padding
|
||||
// ),
|
||||
// );
|
||||
// // Aggiorna lo zoom corrente dopo il fit
|
||||
// _currentZoom = _mapController.camera.zoom;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
|
||||
// @override
|
||||
// Widget build(BuildContext context) {
|
||||
// // Puoi usare Obx per reagire ai cambiamenti in AuthProvider se il profilo può cambiare dinamicamente
|
||||
// print( _areas);
|
||||
// return CardWidget(
|
||||
// height: 700,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// Row(
|
||||
// mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
// children: [
|
||||
// Text(
|
||||
// 'Vigne',
|
||||
// style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.green[800]),
|
||||
// ),
|
||||
// DropdownButton<String>(
|
||||
// value: _selectedMapType,
|
||||
// items: [
|
||||
// DropdownMenuItem(value: 'Standard', child: const Text('Standard')),
|
||||
// DropdownMenuItem(value: 'Terreno (OpenTopoMap)', child: const Text('Terreno')),
|
||||
// DropdownMenuItem(value: 'Satellite (ESRI)', child: const Text('Satellite')),
|
||||
// ],
|
||||
// onChanged: (String? newValue) {
|
||||
// // Chiamata a setState() per aggiornare l'UI
|
||||
// setState(() {
|
||||
// _selectedMapType = newValue; // newValue può essere null, ma il DropdownButton lo gestisce
|
||||
// });
|
||||
// },
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// const SizedBox(height: 16),
|
||||
// Expanded(
|
||||
// child: Container(
|
||||
// // Rimuovi height: 500 se Expanded è sufficiente per il layout
|
||||
// decoration: BoxDecoration(
|
||||
// border: Border.all(color: Colors.grey),
|
||||
// borderRadius: BorderRadius.circular(8.0),
|
||||
// ),
|
||||
// child: FlutterMap(
|
||||
// mapController: _mapController,
|
||||
// options: MapOptions(
|
||||
// initialCenter:initialMapCenter, // Centro iniziale (es. Milano)
|
||||
// initialZoom: 10.0,
|
||||
// ),
|
||||
// children: [
|
||||
// TileLayer(
|
||||
// // Usa l'operatore '!' perché _selectedMapType è garantito non essere null qui
|
||||
// // dato che ha un valore iniziale e il DropdownButton non permette null.
|
||||
// urlTemplate: _mapUrlTemplates[_selectedMapType]!,
|
||||
// userAgentPackageName: 'com.example.vigna_app',
|
||||
// ),
|
||||
// RichAttributionWidget(
|
||||
// attributions: [
|
||||
// TextSourceAttribution(
|
||||
// (_mapAttributions[_selectedMapType]! as Text).data!,
|
||||
// onTap: () => print('Attribution tapped: ${(_mapAttributions[_selectedMapType]! as Text).data}'),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// // MarkerLayer per i vigneti
|
||||
// MarkerLayer(
|
||||
// markers: _vineyardList.map((vineyard) {
|
||||
// return Marker(
|
||||
// width: 40.0,
|
||||
// height: 40.0,
|
||||
// point: vineyard.coordinates != null
|
||||
// ? LatLng(vineyard.coordinates!.latitude, vineyard.coordinates!.longitude)
|
||||
// : const LatLng(0, 0),
|
||||
// child: Tooltip( // Aggiunto Tooltip per mostrare il nome del vigneto al tap
|
||||
// message: vineyard.name,
|
||||
// child: const Icon(Icons.grass, color: Colors.green, size: 30.0),
|
||||
// ),
|
||||
// );
|
||||
// }).toList(),
|
||||
// ),
|
||||
// Positioned(
|
||||
// top: 40,
|
||||
// right: 10,
|
||||
// child: Column(
|
||||
// children: [
|
||||
// FloatingActionButton(
|
||||
// heroTag: 'zoomIn',
|
||||
// mini: true,
|
||||
// onPressed: _zoomIn,
|
||||
// child: Icon(Icons.add),
|
||||
// ),
|
||||
// SizedBox(height: 8),
|
||||
// FloatingActionButton(
|
||||
// heroTag: 'zoomOut',
|
||||
// mini: true,
|
||||
// onPressed: _zoomOut,
|
||||
// child: Icon(Icons.remove),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// if (_areas.isNotEmpty) // Mostra il layer solo se ci sono aree da disegnare
|
||||
// PolygonLayer(
|
||||
// polygons: _areas.expand((areaMap) {
|
||||
// // Converte la Map<int, Coordinates> in List<LatLng>
|
||||
// final List<LatLng> polygonPoints = areaMap.values
|
||||
// .map((coord) => LatLng(coord.latitude, coord.longitude))
|
||||
// .toList();
|
||||
|
||||
// // FlutterMap PolygonLayer richiede che il poligono sia chiuso.
|
||||
// // Ovvero, il primo e l'ultimo punto devono essere uguali.
|
||||
// // Aggiungiamo il primo punto alla fine se non è già così.
|
||||
// if (polygonPoints.isNotEmpty && polygonPoints.first != polygonPoints.last) {
|
||||
// polygonPoints.add(polygonPoints.first);
|
||||
// }
|
||||
|
||||
// // Restituisci una lista di Polygon, uno per ogni area
|
||||
// return [
|
||||
// if (polygonPoints.length >= 3) // Un poligono valido ha almeno 3 punti
|
||||
// Polygon(
|
||||
// points: polygonPoints,
|
||||
// color: Colors.blue.withOpacity(0.3), // Colore di riempimento dell'area
|
||||
// borderStrokeWidth: 2, // Spessore del bordo
|
||||
// borderColor: Colors.blue[900]!, // Colore del bordo
|
||||
// ),
|
||||
// ];
|
||||
// }).toList(),
|
||||
// ),
|
||||
// ],
|
||||
// ),
|
||||
// ),
|
||||
// ),
|
||||
// // Questa SizedBox era posizionata male, l'ho rimossa o spostata se necessaria altrove.
|
||||
// // Se volevi spazio alla destra della mappa, dovrebbe essere nel Row con Expanded.
|
||||
// // const SizedBox(width: 16), // Rimosso o riposizionato se necessario
|
||||
// ],
|
||||
// ),
|
||||
// );
|
||||
// }
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class VignaMapWidget extends StatefulWidget {
|
||||
final List<LatLng> polygonPoints;
|
||||
final MapTapCallback onMapTap;
|
||||
final VoidCallback onClearPolygon;
|
||||
final double calculatedArea;
|
||||
|
||||
const VignaMapWidget({
|
||||
super.key,
|
||||
required this.polygonPoints,
|
||||
required this.onMapTap,
|
||||
required this.onClearPolygon,
|
||||
required this.calculatedArea,
|
||||
});
|
||||
|
||||
@override
|
||||
State<VignaMapWidget> createState() => _VignaMapWidgetState();
|
||||
}
|
||||
|
||||
class _VignaMapWidgetState extends State<VignaMapWidget> {
|
||||
final MapController _mapController = MapController();
|
||||
|
||||
String _selectedMapType = 'Standard'; // Default: OpenStreetMap Standard
|
||||
|
||||
final Map<String, String> _mapUrlTemplates = {
|
||||
'Standard': 'https://tile.openstreetmap.org/{z}/{x}/{y}.png',
|
||||
'Terreno (OpenTopoMap)': 'https://opentopomap.org/{z}/{x}/{y}.png',
|
||||
'Satellite (ESRI)': 'https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}',
|
||||
};
|
||||
|
||||
final Map<String, Widget> _mapAttributions = {
|
||||
'Standard': const Text('© OpenStreetMap contributors'),
|
||||
'Terreno (OpenTopoMap)': const Text('Kartendaten: © OpenStreetMap-Mitwirkende, SRTM | Kartendarstellung: © OpenTopoMap (CC-BY-SA)'),
|
||||
'Satellite (ESRI)': const Text('Tiles © Esri — Source: Esri, i-cubed, USDA FSA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'),
|
||||
};
|
||||
|
||||
@override
|
||||
Widget build(BuildContext context) {
|
||||
return CardWidget(
|
||||
height: 700,
|
||||
child: Padding(
|
||||
padding: const EdgeInsets.all(16.0),
|
||||
child: Column(
|
||||
crossAxisAlignment: CrossAxisAlignment.start,
|
||||
children: [
|
||||
Row(
|
||||
mainAxisAlignment: MainAxisAlignment.spaceBetween,
|
||||
children: [
|
||||
Text(
|
||||
'Mappa Vigna (Disegna Poligono)',
|
||||
style: Theme.of(context).textTheme.headlineSmall?.copyWith(color: Colors.green[800]),
|
||||
),
|
||||
// Dropdown per cambiare il tipo di mappa
|
||||
DropdownButtonHideUnderline(
|
||||
child: DropdownButton<String>(
|
||||
value: _selectedMapType,
|
||||
dropdownColor: Colors.green[700],
|
||||
style: const TextStyle(color: Colors.white, fontSize: 16),
|
||||
icon: const Icon(Icons.map, color: Colors.green), // Icona qui, non nell'AppBar
|
||||
items: _mapUrlTemplates.keys.map((String value) {
|
||||
return DropdownMenuItem<String>(
|
||||
value: value,
|
||||
child: Text(value),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged: (String? newValue) {
|
||||
setState(() {
|
||||
_selectedMapType = newValue!;
|
||||
});
|
||||
},
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
const SizedBox(height: 32),
|
||||
Container(
|
||||
height: 300,
|
||||
decoration: BoxDecoration(
|
||||
border: Border.all(color: Colors.grey),
|
||||
borderRadius: BorderRadius.circular(8.0),
|
||||
),
|
||||
child: FlutterMap(
|
||||
mapController: _mapController,
|
||||
options: MapOptions(
|
||||
initialCenter: const LatLng(45.4642, 9.1900), // Centro iniziale (es. Milano)
|
||||
initialZoom: 10.0,
|
||||
onTap: widget.onMapTap, // Usa la callback passata
|
||||
),
|
||||
children: [
|
||||
TileLayer(
|
||||
urlTemplate: _mapUrlTemplates[_selectedMapType]!,
|
||||
userAgentPackageName: 'com.example.vigna_app',
|
||||
),
|
||||
RichAttributionWidget(
|
||||
attributions: [
|
||||
TextSourceAttribution(
|
||||
(_mapAttributions[_selectedMapType]! as Text).data!,
|
||||
onTap: () => print('Attribution tapped: ${(_mapAttributions[_selectedMapType]! as Text).data}'),
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.polygonPoints.isNotEmpty)
|
||||
PolygonLayer(
|
||||
polygons: [
|
||||
Polygon(
|
||||
points: widget.polygonPoints,
|
||||
color: Colors.green.withOpacity(0.5),
|
||||
borderStrokeWidth: 3,
|
||||
borderColor: Colors.green[900]!,
|
||||
),
|
||||
],
|
||||
),
|
||||
if (widget.polygonPoints.isNotEmpty)
|
||||
MarkerLayer(
|
||||
markers: widget.polygonPoints.map((point) {
|
||||
return Marker(
|
||||
width: 40.0,
|
||||
height: 40.0,
|
||||
point: point,
|
||||
child: const Icon(
|
||||
Icons.grass,
|
||||
color: Colors.red,
|
||||
size: 30.0,
|
||||
),
|
||||
);
|
||||
}).toList(),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
const SizedBox(height: 10),
|
||||
Align(
|
||||
alignment: Alignment.centerRight,
|
||||
child: ElevatedButton.icon(
|
||||
onPressed: widget.onClearPolygon, // Usa la callback passata
|
||||
icon: const Icon(Icons.clear),
|
||||
label: const Text('Resetta Poligono'),
|
||||
style: ElevatedButton.styleFrom(
|
||||
backgroundColor: Colors.red[400],
|
||||
foregroundColor: Colors.white,
|
||||
),
|
||||
),
|
||||
),
|
||||
if (widget.calculatedArea > 0)
|
||||
Padding(
|
||||
padding: const EdgeInsets.only(top: 10.0),
|
||||
child: Text(
|
||||
'Area Calcolata: ${widget.calculatedArea.toStringAsFixed(2)} ettari',
|
||||
style: TextStyle(
|
||||
fontSize: 16,
|
||||
fontWeight: FontWeight.bold,
|
||||
color: Colors.green[800],
|
||||
),
|
||||
),
|
||||
),
|
||||
],
|
||||
),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user