commit
2c5d732477
|
|
@ -1,9 +1,7 @@
|
||||||
import { useAreaStore } from "@/state/areaStore";
|
|
||||||
import { css, keyframes } from "@emotion/react";
|
import { css, keyframes } from "@emotion/react";
|
||||||
import { Loader2 } from "lucide-react";
|
import { Loader2 } from "lucide-react";
|
||||||
import React, { useState } from "react";
|
|
||||||
|
|
||||||
interface Building {
|
export interface Building {
|
||||||
id: number;
|
id: number;
|
||||||
tags: { [key: string]: string | undefined };
|
tags: { [key: string]: string | undefined };
|
||||||
geometry?: { lat: number; lng: number }[];
|
geometry?: { lat: number; lng: number }[];
|
||||||
|
|
@ -14,85 +12,37 @@ from { transform: rotate(0deg); }
|
||||||
to { transform: rotate(360deg); }
|
to { transform: rotate(360deg); }
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export function BuildingHeights({ area }: { area: any }) {
|
export function BuildingHeights({
|
||||||
const [buildings, setBuildings] = useState<Building[]>([]);
|
buildings,
|
||||||
const [loading, setLoading] = useState(false);
|
loading,
|
||||||
|
}: {
|
||||||
const appendAreas = useAreaStore((state) => state.appendAreas);
|
buildings: Building[];
|
||||||
|
loading: boolean;
|
||||||
const requestBuildings = () => {
|
}) {
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
const south = area[1].lat;
|
|
||||||
const west = area[1].lng;
|
|
||||||
const north = area[0].lat;
|
|
||||||
const east = area[0].lng;
|
|
||||||
console.log(south, west, north, east);
|
|
||||||
const query = `[out:json][timeout:25];(way["building"]( ${south},${west},${north},${east} );relation["building"]( ${south},${west},${north},${east} ););out body geom;`;
|
|
||||||
fetch("https://overpass-api.de/api/interpreter", {
|
|
||||||
method: "POST",
|
|
||||||
body: query,
|
|
||||||
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
|
||||||
})
|
|
||||||
.then((response) => response.json())
|
|
||||||
.then((data) => {
|
|
||||||
const blds: any = data.elements.map((element) => ({
|
|
||||||
id: element.id,
|
|
||||||
tags: element.tags,
|
|
||||||
geometry: element.geometry
|
|
||||||
? element.geometry.map((pt) => ({ lat: pt.lat, lng: pt.lon }))
|
|
||||||
: undefined,
|
|
||||||
}));
|
|
||||||
setBuildings(blds);
|
|
||||||
appendAreas(blds);
|
|
||||||
|
|
||||||
console.log("Building Data:", blds);
|
|
||||||
})
|
|
||||||
.catch((error) => {
|
|
||||||
console.error("Error fetching building data:", error);
|
|
||||||
})
|
|
||||||
.finally(() => {
|
|
||||||
setLoading(false);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
css={css({
|
css={css({
|
||||||
position: "relative",
|
position: "relative",
|
||||||
})}
|
})}
|
||||||
>
|
>
|
||||||
<button
|
{loading && (
|
||||||
css={css({
|
<div
|
||||||
color: "#ffffff",
|
css={css({
|
||||||
|
display: "flex",
|
||||||
backgroundColor: "#007bffe8",
|
alignItems: "center",
|
||||||
backdropFilter: "blur(8px)",
|
gap: "0.5rem",
|
||||||
border: "none",
|
color: "#555",
|
||||||
padding: "0.5rem 1rem",
|
})}
|
||||||
borderRadius: "8px",
|
>
|
||||||
outline: "#086ad4c2 solid 0.1rem",
|
|
||||||
cursor: "pointer",
|
|
||||||
transition: "0.2s",
|
|
||||||
display: "flex",
|
|
||||||
alignItems: "center",
|
|
||||||
gap: "0.5rem",
|
|
||||||
":hover": {
|
|
||||||
backgroundColor: "#085fbd",
|
|
||||||
},
|
|
||||||
})}
|
|
||||||
onClick={requestBuildings}
|
|
||||||
>
|
|
||||||
{loading && (
|
|
||||||
<Loader2
|
<Loader2
|
||||||
css={css({
|
css={css({
|
||||||
animation: `${spinAnimation} 1s linear infinite`,
|
animation: `${spinAnimation} 1s linear infinite`,
|
||||||
})}
|
})}
|
||||||
size={16}
|
size={16}
|
||||||
/>
|
/>
|
||||||
)}
|
Fetching building information...
|
||||||
request
|
</div>
|
||||||
</button>
|
)}
|
||||||
<ul
|
<ul
|
||||||
css={css({
|
css={css({
|
||||||
overflow: "scroll",
|
overflow: "scroll",
|
||||||
|
|
|
||||||
|
|
@ -7,8 +7,8 @@ type AreaStore = {
|
||||||
lng: number;
|
lng: number;
|
||||||
}[];
|
}[];
|
||||||
|
|
||||||
appendAreas: (areas: []) => void;
|
appendAreas: (areas: any[]) => void;
|
||||||
setCenter: (center: []) => void;
|
setCenter: (center: any[]) => void;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const useAreaStore = create<AreaStore>((set) => ({
|
export const useAreaStore = create<AreaStore>((set) => ({
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ function Building({
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
}}
|
}}
|
||||||
rotation={[-Math.PI / 2, 0, 0]}
|
rotation={[-Math.PI / 2, 0, 0]}
|
||||||
|
userData={{ exportToGLB: true }}
|
||||||
>
|
>
|
||||||
<extrudeGeometry args={[shape, extrudeSettings]} />
|
<extrudeGeometry args={[shape, extrudeSettings]} />
|
||||||
<meshStandardMaterial color={hovered || clicked ? "#007bff" : "#9da0a3"} />
|
<meshStandardMaterial color={hovered || clicked ? "#007bff" : "#9da0a3"} />
|
||||||
|
|
@ -353,7 +354,14 @@ function Roads({ area }: { area: any }) {
|
||||||
|
|
||||||
const lineGeometry: any = new THREE.BufferGeometry().setFromPoints(points);
|
const lineGeometry: any = new THREE.BufferGeometry().setFromPoints(points);
|
||||||
|
|
||||||
return <Line points={points} color="#34f516" lineWidth={1}></Line>;
|
return (
|
||||||
|
<Line
|
||||||
|
points={points}
|
||||||
|
color="#34f516"
|
||||||
|
lineWidth={1}
|
||||||
|
userData={{ exportToGLB: true }}
|
||||||
|
></Line>
|
||||||
|
);
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|
@ -391,15 +399,16 @@ export function Export() {
|
||||||
};
|
};
|
||||||
|
|
||||||
const exportGLB = () => {
|
const exportGLB = () => {
|
||||||
const sceneClone = scene.clone(true);
|
const exportRoot = new THREE.Group();
|
||||||
sceneClone.traverse((child) => {
|
scene.traverse((child) => {
|
||||||
if (child.userData && child.userData.skipExport === true) child.parent?.remove(child);
|
if (child.userData?.exportToGLB === true) {
|
||||||
if ((child as any).isHtml === true) child.parent?.remove(child);
|
exportRoot.add(child.clone(true));
|
||||||
|
}
|
||||||
});
|
});
|
||||||
const exporter = new GLTFExporter();
|
const exporter = new GLTFExporter();
|
||||||
const options = { binary: true, embedImages: true };
|
const options = { binary: true, embedImages: true };
|
||||||
exporter.parse(
|
exporter.parse(
|
||||||
sceneClone,
|
exportRoot,
|
||||||
(result) => {
|
(result) => {
|
||||||
if (result instanceof ArrayBuffer) {
|
if (result instanceof ArrayBuffer) {
|
||||||
const blob = new Blob([result], { type: "model/gltf-binary" });
|
const blob = new Blob([result], { type: "model/gltf-binary" });
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
import { css } from "@emotion/react";
|
import { css, keyframes } from "@emotion/react";
|
||||||
import { Space } from "../three/Space";
|
import { Space } from "../three/Space";
|
||||||
import { FullscreenModal } from "../components/FullscreenModal";
|
import { FullscreenModal } from "../components/FullscreenModal";
|
||||||
import { Title } from "@/components/text/Title";
|
import { Title } from "@/components/text/Title";
|
||||||
|
|
@ -11,8 +11,8 @@ import {
|
||||||
NextButton,
|
NextButton,
|
||||||
PrevButton,
|
PrevButton,
|
||||||
} from "@/components/button/BottomButton";
|
} from "@/components/button/BottomButton";
|
||||||
import { BuildingHeights } from "@/components/map/Processing";
|
import { BuildingHeights, Building } from "@/components/map/Processing";
|
||||||
import { ChevronLeft, ChevronRight, Download } from "lucide-react";
|
import { ChevronLeft, ChevronRight, Download, Loader2 } from "lucide-react";
|
||||||
import { useAreaStore } from "@/state/areaStore";
|
import { useAreaStore } from "@/state/areaStore";
|
||||||
import { useActionStore } from "@/state/exportStore";
|
import { useActionStore } from "@/state/exportStore";
|
||||||
import { Modal } from "@/components/modal/Modal";
|
import { Modal } from "@/components/modal/Modal";
|
||||||
|
|
@ -26,6 +26,11 @@ const IconSize = css({
|
||||||
height: "14px",
|
height: "14px",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const spinAnimation = keyframes`
|
||||||
|
from { transform: rotate(0deg); }
|
||||||
|
to { transform: rotate(360deg); }
|
||||||
|
`;
|
||||||
|
|
||||||
function App() {
|
function App() {
|
||||||
const [isNextButtonDisabled, setIsNextButtonDisabled] = useState(true);
|
const [isNextButtonDisabled, setIsNextButtonDisabled] = useState(true);
|
||||||
const [areaData, setAreaData] = useState([]);
|
const [areaData, setAreaData] = useState([]);
|
||||||
|
|
@ -36,8 +41,12 @@ function App() {
|
||||||
const [isFleetLogin, setIsFleetLogin] = useState(false);
|
const [isFleetLogin, setIsFleetLogin] = useState(false);
|
||||||
const [isFleetModal, setIsFleetModal] = useState(false);
|
const [isFleetModal, setIsFleetModal] = useState(false);
|
||||||
const [spaceList, setSpaceList] = useState([]);
|
const [spaceList, setSpaceList] = useState([]);
|
||||||
|
const [buildings, setBuildings] = useState<Building[]>([]);
|
||||||
|
const [isFetchingBuildings, setIsFetchingBuildings] = useState(false);
|
||||||
|
const [hasFetchedBuildings, setHasFetchedBuildings] = useState(false);
|
||||||
|
|
||||||
const setCenter = useAreaStore((state) => state.setCenter);
|
const setCenter = useAreaStore((state) => state.setCenter);
|
||||||
|
const appendAreas = useAreaStore((state) => state.appendAreas);
|
||||||
const setAction = useActionStore((state) => state.setAction);
|
const setAction = useActionStore((state) => state.setAction);
|
||||||
const setFleet = useActionStore((state) => state.setFleet);
|
const setFleet = useActionStore((state) => state.setFleet);
|
||||||
|
|
||||||
|
|
@ -101,18 +110,58 @@ function App() {
|
||||||
setCenter(data);
|
setCenter(data);
|
||||||
console.log(data, "AAEE");
|
console.log(data, "AAEE");
|
||||||
setIsNextButtonDisabled(false);
|
setIsNextButtonDisabled(false);
|
||||||
|
setBuildings([]);
|
||||||
|
setHasFetchedBuildings(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleRemove = () => {
|
const handleRemove = () => {
|
||||||
setAreaData([]);
|
setAreaData([]);
|
||||||
setIsNextButtonDisabled(true);
|
setIsNextButtonDisabled(true);
|
||||||
|
setBuildings([]);
|
||||||
|
setHasFetchedBuildings(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
const handleClickNextStep = () => {
|
const requestBuildings = async () => {
|
||||||
|
setIsFetchingBuildings(true);
|
||||||
|
|
||||||
|
const south = areaData[1].lat;
|
||||||
|
const west = areaData[1].lng;
|
||||||
|
const north = areaData[0].lat;
|
||||||
|
const east = areaData[0].lng;
|
||||||
|
const query = `[out:json][timeout:25];(way["building"]( ${south},${west},${north},${east} );relation["building"]( ${south},${west},${north},${east} ););out body geom;`;
|
||||||
|
try {
|
||||||
|
const response = await fetch("https://overpass-api.de/api/interpreter", {
|
||||||
|
method: "POST",
|
||||||
|
body: query,
|
||||||
|
headers: { "Content-Type": "application/x-www-form-urlencoded" },
|
||||||
|
});
|
||||||
|
const data = await response.json();
|
||||||
|
const blds: Building[] = data.elements.map((element) => ({
|
||||||
|
id: element.id,
|
||||||
|
tags: element.tags,
|
||||||
|
geometry: element.geometry
|
||||||
|
? element.geometry.map((pt) => ({ lat: pt.lat, lng: pt.lon }))
|
||||||
|
: undefined,
|
||||||
|
}));
|
||||||
|
setBuildings(blds);
|
||||||
|
appendAreas(blds);
|
||||||
|
setHasFetchedBuildings(true);
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Error fetching building data:", error);
|
||||||
|
} finally {
|
||||||
|
setIsFetchingBuildings(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleClickNextStep = async () => {
|
||||||
if (step == 0 && checkIsBig()) {
|
if (step == 0 && checkIsBig()) {
|
||||||
setIsWarnModal(true);
|
setIsWarnModal(true);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (step == 1 && !hasFetchedBuildings) {
|
||||||
|
await requestBuildings();
|
||||||
|
return;
|
||||||
|
}
|
||||||
setStep(step + 1);
|
setStep(step + 1);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -153,10 +202,14 @@ function App() {
|
||||||
<Column gap="0.5rem">
|
<Column gap="0.5rem">
|
||||||
<Title>Processing</Title>
|
<Title>Processing</Title>
|
||||||
<Description>
|
<Description>
|
||||||
Click the button below to get the building information.
|
Click Next Step to fetch building information. Once loaded, click
|
||||||
|
Next Step again to view the 3D scene.
|
||||||
</Description>
|
</Description>
|
||||||
|
|
||||||
<BuildingHeights area={areaData} />
|
<BuildingHeights
|
||||||
|
buildings={buildings}
|
||||||
|
loading={isFetchingBuildings}
|
||||||
|
/>
|
||||||
</Column>
|
</Column>
|
||||||
</Column>
|
</Column>
|
||||||
</FullscreenModal>
|
</FullscreenModal>
|
||||||
|
|
@ -167,10 +220,24 @@ function App() {
|
||||||
|
|
||||||
<NextButton
|
<NextButton
|
||||||
isShow={step != 2}
|
isShow={step != 2}
|
||||||
disabled={isNextButtonDisabled}
|
disabled={isNextButtonDisabled || isFetchingBuildings}
|
||||||
onClick={handleClickNextStep}
|
onClick={handleClickNextStep}
|
||||||
>
|
>
|
||||||
Next Step <ChevronRight css={IconSize} />
|
{isFetchingBuildings ? (
|
||||||
|
<>
|
||||||
|
<Loader2
|
||||||
|
css={[
|
||||||
|
IconSize,
|
||||||
|
css({ animation: `${spinAnimation} 1s linear infinite` }),
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
Fetching...
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
Next Step <ChevronRight css={IconSize} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
</NextButton>
|
</NextButton>
|
||||||
|
|
||||||
<NextButton isShow={step == 2} onClick={handleClickExport}>
|
<NextButton isShow={step == 2} onClick={handleClickExport}>
|
||||||
|
|
@ -203,7 +270,7 @@ function App() {
|
||||||
GLB Download <Download css={IconSize} />
|
GLB Download <Download css={IconSize} />
|
||||||
</Button>
|
</Button>
|
||||||
|
|
||||||
{isFleetLogin ? (
|
{/* {isFleetLogin ? (
|
||||||
<Button isShow={true} onClick={loadFleetSpace}>
|
<Button isShow={true} onClick={loadFleetSpace}>
|
||||||
Fleet Interlock
|
Fleet Interlock
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -214,7 +281,7 @@ function App() {
|
||||||
>
|
>
|
||||||
Fleet Login
|
Fleet Login
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)} */}
|
||||||
</Row>
|
</Row>
|
||||||
</Column>
|
</Column>
|
||||||
</Modal>
|
</Modal>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue