-
Notifications
You must be signed in to change notification settings - Fork 18
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #120 from pedrolivaresanchez/feat/mapa-geolocaliza…
…cion Feat/mapa geolocalizacion
- Loading branch information
Showing
11 changed files
with
282 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,2 +1,4 @@ | ||
NEXT_PUBLIC_SUPABASE_URL= | ||
NEXT_PUBLIC_SUPABASE_ANON_KEY= | ||
|
||
API_KEY= |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
import { NextRequest } from 'next/server'; | ||
|
||
const mapsTranslationToDbTowns: { [key: string]: string } = { | ||
Aldaya: 'Aldaia', | ||
'Ribarroja de Turia': 'Riba-roja de Túria', | ||
Benetuser: 'Benetusser', | ||
Benetússer: 'Benetusser', | ||
Benetúser: 'Benetusser', | ||
Toris: 'Turís', | ||
Picaña: 'Picanya', | ||
'La Alcudia': "L'Alcúdia", | ||
'Lugar Nuevo de la Corona': 'Llocnou de la Corona', | ||
'Castellón de la Plana': 'Castelló de la Plana', | ||
Alcudia: "L'Alcúdia", | ||
Guadasuar: 'Guadassuar', | ||
València: 'Valencia', | ||
}; | ||
|
||
const GOOGLE_URL = `https://maps.googleapis.com/maps/api/geocode/json?key=${process.env.API_KEY}&latlng=`; | ||
|
||
export type AddressAndTown = { address: string; town: string }; | ||
|
||
function normalizeData({ address, town }: AddressAndTown): AddressAndTown { | ||
const normalizedTown = Object.keys(mapsTranslationToDbTowns).includes(town) ? mapsTranslationToDbTowns[town] : town; | ||
const normalizedAddress = address.replace(town, normalizedTown); | ||
return { address: normalizedAddress, town: normalizedTown }; | ||
} | ||
|
||
function extractAddressAndTown(googleResponse: any) { | ||
// for response refer to documentation: https://developers.google.com/maps/documentation/geocoding/requests-reverse-geocoding | ||
// it returns many due to inaccuracies but they only differ from street number(normally) - we look for a good result - contains sublocality | ||
let town = ''; | ||
let address = ''; | ||
for (const result of googleResponse['results']) { | ||
for (const addressComponent of result['address_components']) { | ||
let localityFound = false; | ||
|
||
// max three, not really a performance issue | ||
for (const type of addressComponent['types']) { | ||
if (type === 'locality') { | ||
localityFound = true; | ||
town = addressComponent['long_name']; | ||
break; | ||
} | ||
} | ||
|
||
if (localityFound) { | ||
address = result['formatted_address']; | ||
|
||
return normalizeData({ address, town }); | ||
} | ||
} | ||
} | ||
|
||
return { address, town }; | ||
} | ||
|
||
export async function POST(request: NextRequest) { | ||
const body = await request.json(); | ||
if (!body.latitude || !body.longitude) { | ||
return Response.json({ | ||
error: 'Latitude and longitude are mandatory fields!', | ||
}); | ||
} | ||
|
||
try { | ||
const response = await fetch(`${GOOGLE_URL}${body.latitude},${body.longitude}`); | ||
const extractedData = extractAddressAndTown(await response.json()); | ||
|
||
return Response.json(extractedData); | ||
} catch (exception) { | ||
console.error(exception); | ||
return Response.json({ | ||
error: 'An error occured calling google - check logs', | ||
}); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
'use client'; | ||
|
||
import GeoLocationMap, { LngLat } from '@/components/map/GeolocationMap'; | ||
import { useState } from 'react'; | ||
|
||
export type AddressDescriptopr = { address: string; town: string; coordinates: LngLat }; | ||
export type AddressAndTownCallback = (addressAndTown: AddressDescriptopr) => void; | ||
export type AddressMapProps = { | ||
onNewAddressCallback: AddressAndTownCallback; | ||
}; | ||
|
||
export default function AddressMap({ onNewAddressCallback }: AddressMapProps) { | ||
const [address, setAddress] = useState(''); | ||
const [town, setTown] = useState(''); | ||
|
||
const onNewPosition = async (lngLat: LngLat) => { | ||
if (address !== '') { | ||
return; | ||
} | ||
|
||
const response = await fetch('/api/address', { | ||
method: 'POST', | ||
headers: { 'Content-Type': 'application/json' }, | ||
body: JSON.stringify({ | ||
longitude: lngLat.lng, | ||
latitude: lngLat.lat, | ||
}), | ||
}).then((res) => res.json()); | ||
|
||
setAddress(response.address); | ||
setTown(response.town); | ||
if (typeof onNewAddressCallback === 'function') { | ||
onNewAddressCallback({ address: response.address, town: response.town, coordinates: lngLat }); | ||
} | ||
}; | ||
|
||
return ( | ||
<div className="space-y-2"> | ||
<GeoLocationMap onNewPositionCallback={onNewPosition} /> | ||
{/* Address */} | ||
<input | ||
disabled | ||
type="text" | ||
value={address} | ||
onChange={(e) => setAddress(e.target.value)} | ||
className="w-full p-2 border rounded focus:ring-2 focus:ring-green-500 focus:border-green-500" | ||
/> | ||
</div> | ||
); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,99 @@ | ||
import { useEffect, useRef } from 'react'; | ||
import maplibregl from 'maplibre-gl'; | ||
import 'maplibre-gl/dist/maplibre-gl.css'; | ||
|
||
const PAIPORTA_LAT_LNG: [number, number] = [-0.41667, 39.42333]; | ||
|
||
export type LngLat = { lng: number; lat: number }; | ||
export type GeoLocationMapProps = { | ||
onNewPositionCallback: (lngLat: LngLat) => void; | ||
zoom?: number; | ||
}; | ||
|
||
export default function GeoLocationMap({ onNewPositionCallback, zoom = 13 }: GeoLocationMapProps) { | ||
const mapContainerRef = useRef<HTMLDivElement | null>(null); | ||
const mapRef = useRef<maplibregl.Map | null>(null); | ||
|
||
// geolocate control | ||
const geolocateControl = new maplibregl.GeolocateControl({ | ||
positionOptions: { | ||
enableHighAccuracy: true, | ||
}, | ||
trackUserLocation: true, | ||
showAccuracyCircle: true, | ||
}); | ||
|
||
useEffect(() => { | ||
if (!mapRef.current) { | ||
if (!mapContainerRef.current) { | ||
return; | ||
} | ||
|
||
mapRef.current = new maplibregl.Map({ | ||
container: mapContainerRef.current, | ||
style: 'https://basemaps.cartocdn.com/gl/positron-gl-style/style.json', | ||
center: PAIPORTA_LAT_LNG, | ||
zoom: zoom, | ||
}); | ||
|
||
mapRef.current.on('moveend', () => { | ||
if (!mapRef.current) { | ||
return; | ||
} | ||
|
||
const center = mapRef.current.getCenter(); | ||
if (typeof onNewPositionCallback === 'function') { | ||
onNewPositionCallback(center); | ||
} | ||
}); | ||
|
||
mapRef.current.on('move', () => { | ||
if (!mapRef.current) { | ||
return; | ||
} | ||
|
||
const center = mapRef.current.getCenter(); | ||
marker.setLngLat(center); | ||
}); | ||
|
||
geolocateControl.on('geolocate', (e) => { | ||
if (!mapRef.current) { | ||
return; | ||
} | ||
|
||
const userLocation: [number, number] = [e.coords.longitude, e.coords.latitude]; | ||
// Center the map on the user's location | ||
mapRef.current.flyTo({ | ||
center: userLocation, | ||
zoom, | ||
essential: true, | ||
}); | ||
|
||
if (typeof onNewPositionCallback === 'function') { | ||
onNewPositionCallback({ lng: userLocation[0], lat: userLocation[1] }); | ||
} | ||
}); | ||
|
||
const marker = new maplibregl.Marker({ | ||
color: '#ef4444', //text-red-500 | ||
draggable: false, | ||
}) | ||
.setLngLat(mapRef.current.getCenter()) | ||
.addTo(mapRef.current); | ||
|
||
mapRef.current.addControl(new maplibregl.NavigationControl(), 'top-right'); | ||
// Add the geolocate control | ||
mapRef.current.addControl(geolocateControl); | ||
|
||
// add to js queue so that the control is correctly added, then trigger the location detection | ||
setTimeout(() => geolocateControl.trigger(), 100); | ||
} | ||
|
||
return () => { | ||
mapRef.current?.remove(); | ||
mapRef.current = null; | ||
}; | ||
}, [zoom, onNewPositionCallback]); | ||
|
||
return <div ref={mapContainerRef} className="aspect-video w-full" />; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
UPDATE towns | ||
SET name = 'Castelló de la Plana' | ||
WHERE name = 'Castelló'; | ||
|
||
UPDATE help_requests | ||
SET town_id = ( | ||
SELECT id FROM towns WHERE name = 'L''Alcúdia' | ||
) | ||
WHERE town_id = ( | ||
SELECT id FROM towns WHERE name = 'Alcudia' | ||
); | ||
|
||
DELETE FROM towns | ||
WHERE name = 'Alcudia' |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
create policy "Enable insert public" | ||
on "public"."towns" | ||
as permissive | ||
for insert | ||
to public | ||
with check (true); | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters