feat: remove box

This commit is contained in:
Huh Hyeongjun 2025-02-24 16:20:28 +09:00
parent 9b8b3ff401
commit 10c2716aeb
5 changed files with 251 additions and 37 deletions

View File

@ -1,12 +1,15 @@
import { css } from "@emotion/react"; import { css } from "@emotion/react";
import { ButtonHTMLAttributes, DetailedHTMLProps } from "react"; import { ButtonHTMLAttributes, DetailedHTMLProps } from "react";
type ButtonProps = DetailedHTMLProps< interface ButtonProps
ButtonHTMLAttributes<HTMLButtonElement>, extends DetailedHTMLProps<
HTMLButtonElement ButtonHTMLAttributes<HTMLButtonElement>,
>; HTMLButtonElement
> {
isShow?: boolean;
}
export function BottomButton(props: ButtonProps) { export function NextButton(props: ButtonProps) {
return ( return (
<button <button
css={css({ css={css({
@ -25,6 +28,43 @@ export function BottomButton(props: ButtonProps) {
outline: "rgba(240, 240, 244, 0.51) solid 0.1rem", outline: "rgba(240, 240, 244, 0.51) solid 0.1rem",
cursor: "pointer", cursor: "pointer",
transition: "0.2s", transition: "0.2s",
display: props.isShow ? "" : "none",
":hover": {
backgroundColor: "#ebeef0c2",
},
":disabled": {
backgroundColor: "#ebeef0c2",
cursor: "not-allowed",
},
})}
{...props}
>
{props.children}
</button>
);
}
export function PrevButton(props: ButtonProps) {
return (
<button
css={css({
position: "absolute",
zIndex: 9999,
left: "2rem",
bottom: "2rem",
color: "#000000",
backgroundColor: "#ffffff96",
backdropFilter: "blur(8px)",
border: "none",
padding: "0.75rem 1.25rem",
borderRadius: "8px",
fontWeight: "300",
fontSize: "14px",
outline: "rgba(240, 240, 244, 0.51) solid 0.1rem",
cursor: "pointer",
transition: "0.2s",
display: props.isShow ? "" : "none",
":hover": { ":hover": {
backgroundColor: "#ebeef0c2", backgroundColor: "#ebeef0c2",
}, },

View File

@ -1,3 +1,81 @@
export function ProcessingMap() { import { css } from "@emotion/react";
return <p>processing</p>; import React, { useState } from "react";
interface Building {
id: number;
tags: { [key: string]: string | undefined };
geometry?: { lat: number; lng: number }[];
}
export function BuildingHeights({ area }: { area: any }) {
const [buildings, setBuildings] = useState<Building[]>([]);
const requestBuildings = () => {
console.log(area);
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: 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);
console.log("Building Data:", blds);
})
.catch((error) => {
console.error("Error fetching building data:", error);
});
};
return (
<div
css={css({
position: "relative",
})}
>
<button onClick={requestBuildings}>request</button>
<ul
css={css({
overflow: "scroll",
zIndex: 999,
position: "absolute",
top: "1rem",
height: "80vh",
})}
>
{buildings.map((b) => (
<li key={b.id}>
<div>Building {b.id}</div>
<div>Height: {b.tags.height || "No height info"}</div>
<div>Location/Shape:</div>
{b.geometry ? (
<ul>
{b.geometry.map((pt, index) => (
<li key={index}>
({pt.lat.toFixed(5)}, {pt.lng.toFixed(5)})
</li>
))}
</ul>
) : (
<div>No geometry info</div>
)}
<div>Other Tags: {JSON.stringify(b.tags)}</div>
</li>
))}
</ul>
</div>
);
} }

View File

@ -11,12 +11,13 @@ import { css } from "@emotion/react";
function RectangleSelector({ function RectangleSelector({
isDrag = true, isDrag = true,
bounds,
onChange, onChange,
}: { }: {
isDrag: boolean; isDrag: boolean;
bounds: LatLngBounds | null;
onChange: (LatLngBounds: LatLngBounds) => void; onChange: (LatLngBounds: LatLngBounds) => void;
}) { }) {
const [bounds, setBounds] = useState<LatLngBounds | null>(null);
const [firstPoint, setFirstPoint] = useState<LatLng | null>(null); const [firstPoint, setFirstPoint] = useState<LatLng | null>(null);
useEffect(() => { useEffect(() => {
@ -35,12 +36,11 @@ function RectangleSelector({
}, },
mousemove(e) { mousemove(e) {
if (firstPoint) { if (firstPoint) {
setBounds(new L.LatLngBounds(firstPoint, e.latlng)); onChange(new L.LatLngBounds(firstPoint, e.latlng));
} }
}, },
mouseup(e) { mouseup(e) {
if (firstPoint) { if (firstPoint) {
setBounds(new L.LatLngBounds(firstPoint, e.latlng));
onChange(new L.LatLngBounds(firstPoint, e.latlng)); onChange(new L.LatLngBounds(firstPoint, e.latlng));
setFirstPoint(null); setFirstPoint(null);
} }
@ -51,14 +51,28 @@ function RectangleSelector({
) : null; ) : null;
} }
export function MapComponent({ onDone }: { onDone: (e) => void }) { export function MapComponent({
onDone,
onRemove,
}: {
onDone: (e) => void;
onRemove: () => void;
}) {
const [isDrag, setIsDrag] = useState(true); const [isDrag, setIsDrag] = useState(true);
const [bounds, setBounds] = useState<LatLngBounds | null>(null);
const handleClickSwitchDrag = () => { const handleClickSwitchDrag = () => {
setIsDrag(!isDrag); setIsDrag(!isDrag);
}; };
const handleClickRemoveBox = () => {
onRemove();
setBounds(null);
setIsDrag(true);
};
const handleChangeDone = (e) => { const handleChangeDone = (e) => {
setBounds(e);
onDone([e._northEast, e._southWest]); onDone([e._northEast, e._southWest]);
}; };
@ -68,40 +82,80 @@ export function MapComponent({ onDone }: { onDone: (e) => void }) {
position: "relative", position: "relative",
})} })}
> >
<button <div
css={css({ css={css({
position: "absolute", position: "absolute",
zIndex: 9999, zIndex: 9999,
right: "1rem", right: "1rem",
top: "1rem", top: "1rem",
color: "#000000", display: "flex",
backgroundColor: "#ffffff96", justifyContent: "flex-end",
backdropFilter: "blur(8px)", gap: "0.5rem",
border: "none",
padding: "0.5rem 1rem",
borderRadius: "8px",
outline: "rgba(240, 240, 244, 0.51) solid 0.1rem",
cursor: "pointer",
transition: "0.2s",
":hover": {
backgroundColor: "#ebeef0c2",
},
})} })}
onClick={handleClickSwitchDrag}
> >
{isDrag ? "Disable" : "Enable"} Drag <button
</button> css={css({
display: isDrag ? "none" : "",
color: "#ffffff",
backgroundColor: "#ef4444",
backdropFilter: "blur(8px)",
border: "none",
padding: "0.5rem 1rem",
borderRadius: "8px",
outline: "#ef4444c2 solid 0.1rem",
cursor: "pointer",
transition: "0.2s",
":hover": {
backgroundColor: "#ef4444",
},
})}
onClick={handleClickRemoveBox}
>
Remove Box
</button>
<button
css={css({
color: isDrag ? "#ffffff" : "#000000",
backgroundColor: isDrag ? "#007bffe8" : "#ffffff96",
backdropFilter: "blur(8px)",
border: "none",
padding: "0.5rem 1rem",
borderRadius: "8px",
outline: isDrag
? "#086ad4c2 solid 0.1rem"
: "rgba(240, 240, 244, 0.51) solid 0.1rem",
cursor: "pointer",
transition: "0.2s",
":hover": {
backgroundColor: isDrag ? "#085fbd" : "#ebeef0c2",
},
})}
onClick={handleClickSwitchDrag}
>
{isDrag ? "Select Box" : "Back to Drag"}
</button>
</div>
<MapContainer <MapContainer
center={[36.48656, 127.29064]} center={[36.48656, 127.29064]}
zoom={13} zoom={13}
style={{ height: "70vh", width: "100%" }} style={{
height: "70vh",
width: "100%",
}}
> >
<TileLayer <TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors' attribution='&copy; <a href="https://www.openstreetmap.org/">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png" url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/> />
<RectangleSelector isDrag={isDrag} onChange={handleChangeDone} /> <RectangleSelector
bounds={bounds}
isDrag={isDrag}
onChange={handleChangeDone}
/>
</MapContainer> </MapContainer>
</div> </div>
); );

View File

@ -14,6 +14,11 @@ body,
-moz-user-select: none; -moz-user-select: none;
-ms-user-select: none; -ms-user-select: none;
user-select: none; user-select: none;
-ms-overflow-style: none;
}
::-webkit-scrollbar {
display: none;
} }
* { * {

View File

@ -6,20 +6,36 @@ import { Description } from "@/components/text/Description";
import { Column } from "@/components/flex/Column"; import { Column } from "@/components/flex/Column";
import { MapComponent } from "@/components/map/SelectMap"; import { MapComponent } from "@/components/map/SelectMap";
import { useState } from "react"; import { useState } from "react";
import { BottomButton } from "@/components/button/BottomButton"; import { NextButton, PrevButton } from "@/components/button/BottomButton";
import { BuildingHeights } from "@/components/map/Processing";
function App() { function App() {
const [isNextButtonDisabled, setIsNextButtonDisabled] = useState(true); const [isNextButtonDisabled, setIsNextButtonDisabled] = useState(true);
const handleDone = (e) => { const [areaData, setAreaData] = useState([]);
console.log(e); const [steps, setSteps] = useState(["front", "processing"]);
const [step, setStep] = useState(0);
const handleDone = (data) => {
setAreaData(data);
setIsNextButtonDisabled(false); setIsNextButtonDisabled(false);
}; };
const handleClickNextStep = () => {}; const handleRemove = () => {
setAreaData([]);
setIsNextButtonDisabled(true);
};
const handleClickNextStep = () => {
setStep(step + 1);
};
const handleClickPrevStep = () => {
setStep(step - 1);
};
return ( return (
<div css={css({ height: "100%", width: "100%" })}> <div css={css({ height: "100%", width: "100%" })}>
<FullscreenModal isOpen={true}> <FullscreenModal isOpen={steps[step] == "front"}>
<Column gap="1rem"> <Column gap="1rem">
<Column gap="0.5rem"> <Column gap="0.5rem">
<Title>Generate 3d map</Title> <Title>Generate 3d map</Title>
@ -27,16 +43,37 @@ function App() {
Tools to create 3D maps based on maps and export them in 3D format Tools to create 3D maps based on maps and export them in 3D format
</Description> </Description>
</Column> </Column>
<MapComponent onDone={handleDone}></MapComponent> <MapComponent
onRemove={handleRemove}
onDone={handleDone}
></MapComponent>
</Column> </Column>
</FullscreenModal> </FullscreenModal>
<BottomButton <FullscreenModal isOpen={steps[step] == "processing"}>
<Column gap="1rem">
<Column gap="0.5rem">
<Title>Processing</Title>
<Description>
Tools to create 3D maps based on maps and export them in 3D format
</Description>
<BuildingHeights area={areaData} />
</Column>
</Column>
</FullscreenModal>
<PrevButton isShow={step != 0} onClick={handleClickPrevStep}>
Prev Step
</PrevButton>
<NextButton
isShow={true}
disabled={isNextButtonDisabled} disabled={isNextButtonDisabled}
onClick={handleClickNextStep} onClick={handleClickNextStep}
> >
Next Step Next Step
</BottomButton> </NextButton>
<Space /> <Space />
</div> </div>