| 313 | } |
| 314 | |
| 315 | function Roads({ area }: { area: any }) { |
| 316 | const [roads, setRoads] = useState<any[]>([]); |
| 317 | if (!area || area.length < 2) return null; |
| 318 | const refLat = (area[1].lat + area[0].lat) / 2; |
| 319 | const refLng = (area[1].lng + area[0].lng) / 2; |
| 320 | |
| 321 | function project(lat: number, lng: number) { |
| 322 | const x = (lng - refLng) * scale * Math.cos((refLat * Math.PI) / 180); |
| 323 | const y = (lat - refLat) * scale; |
| 324 | return new THREE.Vector2(x, y); |
| 325 | } |
| 326 | |
| 327 | useEffect(() => { |
| 328 | const south = area[1].lat; |
| 329 | const west = area[1].lng; |
| 330 | const north = area[0].lat; |
| 331 | const east = area[0].lng; |
| 332 | const query = `[out:json][timeout:25];(way["highway"](${south},${west},${north},${east}););out body geom;`; |
| 333 | fetch("https://overpass-api.de/api/interpreter", { |
| 334 | method: "POST", |
| 335 | body: query, |
| 336 | headers: { "Content-Type": "application/x-www-form-urlencoded" }, |
| 337 | }) |
| 338 | .then((response) => response.json()) |
| 339 | .then((data) => { |
| 340 | setRoads(data.elements); |
| 341 | }) |
| 342 | .catch((err) => console.error(err)); |
| 343 | }, [area]); |
| 344 | |
| 345 | return ( |
| 346 | <> |
| 347 | {roads.map((road, index) => { |
| 348 | if (!road.geometry || road.geometry.length < 2) return null; |
| 349 | |
| 350 | const points = road.geometry.map((pt: any) => { |
| 351 | const v = project(pt.lat, pt.lon); |
| 352 | return new THREE.Vector3(v.x, 0.1, -v.y); |
| 353 | }); |
| 354 | |
| 355 | const lineGeometry: any = new THREE.BufferGeometry().setFromPoints(points); |
| 356 | |
| 357 | return ( |
| 358 | <Line |
| 359 | points={points} |
| 360 | color="#34f516" |
| 361 | lineWidth={1} |
| 362 | userData={{ exportToGLB: true }} |
| 363 | ></Line> |
| 364 | ); |
| 365 | })} |
| 366 | </> |
| 367 | ); |
| 368 | } |
| 369 | |
| 370 | export function Export() { |
| 371 | const { scene } = useThree(); |