Add Leaflet fallback for map rendering and implement group visibility toggling
This commit is contained in:
@@ -4,6 +4,38 @@ import { check, PERMISSIONS, request, requestMultiple, RESULTS } from 'react-nat
|
||||
import MapView, { Marker, UrlTile, Region } from 'react-native-maps';
|
||||
import Geolocation from '@react-native-community/geolocation';
|
||||
|
||||
// Generate a small Leaflet page showing markers (used as Android fallback)
|
||||
const makeLeafletHtml = (centerLat: number, centerLng: number, pointsJson: string) => `<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0" />
|
||||
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
|
||||
<style>html,body,#map{height:100%;margin:0;padding:0}#map{width:100%;height:100%}.legend{position: absolute; top:8px; right:8px; background:#fff;padding:6px;border-radius:6px;font-family:sans-serif;font-size:12px;box-shadow:0 1px 4px rgba(0,0,0,0.2)}</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="map"></div>
|
||||
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
|
||||
<script>
|
||||
(function(){
|
||||
const center = [${centerLat}, ${centerLng}];
|
||||
const points = ${pointsJson};
|
||||
const map = L.map('map',{zoomControl:true,attributionControl:false}).setView(center,13);
|
||||
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {maxZoom: 19}).addTo(map);
|
||||
points.forEach(p => {
|
||||
L.circleMarker([p.latitude, p.longitude], { radius: 7, color: p.color, fillColor: p.color, fillOpacity: 0.9 }).addTo(map).bindPopup(p.title || '');
|
||||
});
|
||||
var legend = L.control({position: 'topright'});
|
||||
legend.onAdd = function (map) {
|
||||
var div = L.DomUtil.create('div', 'legend');
|
||||
div.innerHTML = '<b>Groups</b><br/>' + (Array.from(new Set(points.map(p => p.group))).map(g => '<div style="display:flex;align-items:center;margin-top:6px"><span style="width:10px;height:10px;background:'+points.find(x=>x.group===g).color+';display:inline-block;margin-right:6px;border-radius:2px"></span>'+g+'</div>').join(''));
|
||||
return div;
|
||||
};
|
||||
legend.addTo(map);
|
||||
})();
|
||||
</script>
|
||||
</body>
|
||||
</html>`;
|
||||
|
||||
type Props = { onClose?: () => void };
|
||||
|
||||
class ErrorBoundary extends React.Component<any, { error: any }> {
|
||||
@@ -41,6 +73,21 @@ export default function Map({ onClose }: Props) {
|
||||
const [mapAttempt, setMapAttempt] = useState(false);
|
||||
const [mapError, setMapError] = useState<any>(null);
|
||||
|
||||
// --- Sample points and group visibility state ---
|
||||
const SAMPLE_POINTS = [
|
||||
{ id: 'p1', latitude: 22.3039, longitude: 70.8022, group: 'Restaurants', title: 'Restaurant A' },
|
||||
{ id: 'p2', latitude: 22.3090, longitude: 70.8070, group: 'Restaurants', title: 'Restaurant B' },
|
||||
{ id: 'p3', latitude: 22.2980, longitude: 70.8000, group: 'Parks', title: 'Park A' },
|
||||
{ id: 'p4', latitude: 22.3140, longitude: 70.7950, group: 'Hospitals', title: 'Hospital A' },
|
||||
{ id: 'p5', latitude: 22.3100, longitude: 70.8100, group: 'Parks', title: 'Park B' },
|
||||
];
|
||||
const [points] = useState(SAMPLE_POINTS);
|
||||
const groupList = Array.from(new Set(points.map(p => p.group)));
|
||||
const colorPalette = ['#ef4444', '#f59e0b', '#10b981', '#3b82f6', '#8b5cf6'];
|
||||
const getGroupColor = (group: string) => colorPalette[groupList.indexOf(group) % colorPalette.length];
|
||||
const [groupsVisible, setGroupsVisible] = useState<Record<string, boolean>>(() => Object.fromEntries(groupList.map(g => [g, true])));
|
||||
const toggleGroup = (g: string) => setGroupsVisible(prev => ({ ...prev, [g]: !prev[g] }));
|
||||
|
||||
useEffect(() => {
|
||||
const init = async () => {
|
||||
setLoading(true);
|
||||
@@ -170,18 +217,7 @@ export default function Map({ onClose }: Props) {
|
||||
if (permissionStatus === 'granted') getCurrentLocation();
|
||||
else retryPermission();
|
||||
};
|
||||
return (
|
||||
<MapView
|
||||
style={{ flex: 1 }}
|
||||
initialRegion={{
|
||||
latitude: 22.3039,
|
||||
longitude: 70.8022,
|
||||
latitudeDelta: 0.05,
|
||||
longitudeDelta: 0.05,
|
||||
}}
|
||||
/>
|
||||
|
||||
)
|
||||
return (
|
||||
<View style={styles.container}>
|
||||
<TouchableOpacity style={styles.closeButton} onPress={onClose}>
|
||||
@@ -216,20 +252,36 @@ export default function Map({ onClose }: Props) {
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
) : mapAttempt ? (
|
||||
<ErrorBoundary onError={(e: any) => { setMapError(e); console.error('Map render error:', e); }}>
|
||||
<MapView style={styles.map} initialRegion={region as Region} showsUserLocation={true} showsMyLocationButton={true}>
|
||||
<UrlTile
|
||||
urlTemplate="https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
maximumZ={19}
|
||||
flipY={false}
|
||||
tileSize={256}
|
||||
/>
|
||||
<Marker coordinate={{ latitude: region.latitude, longitude: region.longitude }} title="You are here" />
|
||||
</MapView>
|
||||
<TouchableOpacity style={styles.osmAttribution} onPress={() => Linking.openURL('https://www.openstreetmap.org/copyright')}>
|
||||
<Text style={styles.osmAttributionText}>© OpenStreetMap</Text>
|
||||
</TouchableOpacity>
|
||||
</ErrorBoundary>
|
||||
<View style={{ flex: 1 }}>
|
||||
<View style={styles.legendContainer} pointerEvents="box-none">
|
||||
{groupList.map(g => (
|
||||
<TouchableOpacity key={g} style={[styles.legendItem, !groupsVisible[g] && styles.legendItemDisabled]} onPress={() => toggleGroup(g)}>
|
||||
<View style={[styles.legendDot, { backgroundColor: getGroupColor(g) }]} />
|
||||
<Text style={styles.legendLabel}>{g}</Text>
|
||||
</TouchableOpacity>
|
||||
))}
|
||||
</View>
|
||||
|
||||
<ErrorBoundary onError={(e: any) => { setMapError(e); console.error('Map render error:', e); }}>
|
||||
|
||||
<MapView style={styles.map} initialRegion={region as Region} showsUserLocation={true} showsMyLocationButton={true}>
|
||||
<UrlTile
|
||||
urlTemplate="https://a.tile.openstreetmap.org/{z}/{x}/{y}.png"
|
||||
maximumZ={19}
|
||||
flipY={false}
|
||||
tileSize={256}
|
||||
/>
|
||||
{points.filter(p => groupsVisible[p.group]).map(p => (
|
||||
<Marker key={p.id} coordinate={{ latitude: p.latitude, longitude: p.longitude }} title={p.title} pinColor={getGroupColor(p.group)} />
|
||||
))}
|
||||
</MapView>
|
||||
|
||||
|
||||
<TouchableOpacity style={styles.osmAttribution} onPress={() => Linking.openURL('https://www.openstreetmap.org/copyright')}>
|
||||
<Text style={styles.osmAttributionText}>© OpenStreetMap</Text>
|
||||
</TouchableOpacity>
|
||||
</ErrorBoundary>
|
||||
</View>
|
||||
) : (
|
||||
<View style={styles.center}>
|
||||
<Text style={{ marginBottom: 12 }}>Map is ready. Tap "Try Map" to open it.</Text>
|
||||
@@ -257,4 +309,9 @@ const styles = StyleSheet.create({
|
||||
actionText: { color: '#fff', fontWeight: '600' },
|
||||
osmAttribution: { position: 'absolute', bottom: 16, right: 8, backgroundColor: 'rgba(255,255,255,0.9)', padding: 6, borderRadius: 6 },
|
||||
osmAttributionText: { fontSize: 10, color: '#333' },
|
||||
legendContainer: { position: 'absolute', top: 12, right: 12, zIndex: 20, flexDirection: 'column', backgroundColor: 'rgba(255,255,255,0.95)', padding: 8, borderRadius: 8 },
|
||||
legendItem: { flexDirection: 'row', alignItems: 'center', paddingVertical: 6, paddingHorizontal: 8, borderRadius: 6, marginBottom: 6 },
|
||||
legendItemDisabled: { opacity: 0.4 },
|
||||
legendDot: { width: 12, height: 12, borderRadius: 3, marginRight: 8 },
|
||||
legendLabel: { fontSize: 12, color: '#111' },
|
||||
});
|
||||
Reference in New Issue
Block a user