Compare commits
3 commits
f051581171
...
3ab47584bb
| Author | SHA1 | Date | |
|---|---|---|---|
| 3ab47584bb | |||
| 8a36517f08 | |||
| a25000e039 |
3 changed files with 42 additions and 71 deletions
|
|
@ -19,76 +19,76 @@ const baseConfigs: Omit<LayerConfig, 'getUrl'>[] = [
|
|||
radius: 1,
|
||||
topic: 'Parcels',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'green', width: 1.5 }),
|
||||
fill: new Fill({ color: 'rgba(0, 255, 0, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#00D9FF', width: 5.5 }),
|
||||
fill: new Fill({ color: 'rgba(0, 217, 255, 0.1)' }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 100,
|
||||
topic: 'Sewer_Main_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'purple', width: 2 }),
|
||||
stroke: new Stroke({ color: '#FF00FF', width: 2 }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 50,
|
||||
topic: 'Zoning_Base_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'red', width: 1 }),
|
||||
stroke: new Stroke({ color: '#FF3333', width: 1 }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 50,
|
||||
topic: 'Water_Main_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'blue', width: 5 }),
|
||||
stroke: new Stroke({ color: '#0066FF', width: 5 }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 1000,
|
||||
topic: 'Sustainable_Development_Areas',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'green', width: 3 }),
|
||||
stroke: new Stroke({ color: '#00FF66', width: 3 }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 1000,
|
||||
topic: 'Housing_Solution_Areas',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'black', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(255, 200, 0, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#FFD700', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(255, 215, 0, 0.1)' }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 100000,
|
||||
topic: 'Promise_Zones_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'black', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#9933FF', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(153, 51, 255, 0.1)' }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 100000,
|
||||
topic: 'MHPA_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'orange', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#FF6B6B', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(255, 107, 107, 0.1)' }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 100000,
|
||||
topic: 'Coastal_Zones',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'skin', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#FF9966', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(255, 153, 102, 0.1)' }),
|
||||
}),
|
||||
},
|
||||
{
|
||||
radius: 100000,
|
||||
topic: 'Community_Plan_Implementation_Overlay_Zone_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'darkblue', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#4169E1', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(65, 105, 225, 0.1)' }),
|
||||
}),
|
||||
|
||||
},
|
||||
|
|
@ -96,8 +96,8 @@ const baseConfigs: Omit<LayerConfig, 'getUrl'>[] = [
|
|||
radius: 100000,
|
||||
topic: 'Airport_Influence_Area',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'darkyellow', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#FFBB00', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(255, 187, 0, 0.1)' }),
|
||||
}),
|
||||
|
||||
},
|
||||
|
|
@ -105,8 +105,8 @@ const baseConfigs: Omit<LayerConfig, 'getUrl'>[] = [
|
|||
radius: 100000,
|
||||
topic: 'Historic_Districts_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'white', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#C0C0C0', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(192, 192, 192, 0.1)' }),
|
||||
}),
|
||||
|
||||
},
|
||||
|
|
@ -114,8 +114,8 @@ const baseConfigs: Omit<LayerConfig, 'getUrl'>[] = [
|
|||
radius: 100000,
|
||||
topic: 'HRB_Designated_Resources_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'darkgreen', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#00CC66', width: 2.5 }),
|
||||
fill: new Fill({ color: 'rgba(0, 204, 102, 0.1)' }),
|
||||
}),
|
||||
|
||||
},
|
||||
|
|
@ -123,8 +123,8 @@ const baseConfigs: Omit<LayerConfig, 'getUrl'>[] = [
|
|||
radius: 100000,
|
||||
topic: 'Fire_Hazard_Severity_Zones_SD',
|
||||
style: new Style({
|
||||
stroke: new Stroke({ color: 'yellow', width: 1.5 }),
|
||||
fill: new Fill({ color: 'rgba(116, 3, 255, 0.1)' }),
|
||||
stroke: new Stroke({ color: '#FF6600', width: 1.5 }),
|
||||
fill: new Fill({ color: 'rgba(255, 102, 0, 0.1)' }),
|
||||
}),
|
||||
|
||||
},
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
// src/Methods/addMarker.ts
|
||||
|
||||
import Feature from 'ol/Feature';
|
||||
import Point from 'ol/geom/Point';
|
||||
|
|
@ -9,16 +8,11 @@ import Icon from 'ol/style/Icon';
|
|||
import { Map } from 'ol';
|
||||
import { Coordinate } from 'ol/coordinate';
|
||||
|
||||
/**
|
||||
* Adds a custom icon marker to the map at a given coordinate,
|
||||
* replacing the previous marker layer if one exists.
|
||||
*/
|
||||
export const addMarker = (
|
||||
coords: Coordinate,
|
||||
map: Map,
|
||||
existingMarkerLayer: VectorLayer | null
|
||||
): VectorLayer => {
|
||||
// Create feature for the marker
|
||||
const markerFeature = new Feature({
|
||||
geometry: new Point(coords),
|
||||
});
|
||||
|
|
|
|||
|
|
@ -13,16 +13,15 @@ import gsap from 'gsap';
|
|||
import { useNavigate } from 'react-router-dom';
|
||||
import { easeOut } from 'ol/easing';
|
||||
|
||||
// --- THEME CONFIGURATION ---
|
||||
const themes = {
|
||||
dark: {
|
||||
bg: '#050505',
|
||||
glass: 'rgba(20, 20, 20, 0.75)', // Slightly darker for better contrast
|
||||
border: 'rgba(255, 255, 255, 0.15)', // Stronger border
|
||||
glass: 'rgba(20, 20, 20, 0.75)',
|
||||
border: 'rgba(255, 255, 255, 0.15)',
|
||||
textMain: '#ffffff',
|
||||
textMuted: '#AAAAAA',
|
||||
iconColor: '#ffffff',
|
||||
shadow: '0 4px 20px rgba(0,0,0,0.4)', // Strong shadow
|
||||
shadow: '0 4px 20px rgba(0,0,0,0.4)',
|
||||
switchTrackActive: 'rgba(255,255,255,0.2)',
|
||||
switchTrackInactive: 'rgba(255,255,255,0.05)',
|
||||
switchKnobActive: '#fff',
|
||||
|
|
@ -31,7 +30,7 @@ const themes = {
|
|||
},
|
||||
light: {
|
||||
bg: '#F4F4F5',
|
||||
glass: 'rgba(255, 255, 255, 0.90)', // More opaque for visibility
|
||||
glass: 'rgba(255, 255, 255, 0.90)',
|
||||
border: 'rgba(0, 0, 0, 0.1)',
|
||||
textMain: '#18181B',
|
||||
textMuted: '#52525B',
|
||||
|
|
@ -45,7 +44,6 @@ const themes = {
|
|||
}
|
||||
};
|
||||
|
||||
// --- SCROLLBAR CSS ---
|
||||
const scrollbarStyles = `
|
||||
.custom-scrollbar::-webkit-scrollbar { width: 4px; }
|
||||
.custom-scrollbar::-webkit-scrollbar-track { background: rgba(128, 128, 128, 0.1); border-radius: 4px; }
|
||||
|
|
@ -53,18 +51,14 @@ const scrollbarStyles = `
|
|||
.custom-scrollbar::-webkit-scrollbar-thumb:hover { background: rgba(128, 128, 128, 0.5); }
|
||||
`;
|
||||
|
||||
// --- HELPER: Extract Color ---
|
||||
const extractColorFromStyle = (style: any): string => {
|
||||
let color: any = '#888888';
|
||||
if (style.getStroke() && style.getStroke().getColor()) color = style.getStroke().getColor();
|
||||
else if (style.getFill() && style.getFill().getColor()) color = style.getFill().getColor();
|
||||
|
||||
const colorMap: Record<string, string> = {
|
||||
'skin': '#FFCBA4', 'darkyellow': '#FCC200', 'darkblue': '#00008B',
|
||||
'darkgreen': '#006400', 'purple': '#800080', 'orange': '#FFA500',
|
||||
'green': '#00FF00', 'blue': '#0000FF', 'red': '#FF0000', 'black': '#888888',
|
||||
};
|
||||
return colorMap[color] || color;
|
||||
if (style.getStroke() && style.getStroke().getColor()) {
|
||||
return style.getStroke().getColor();
|
||||
}
|
||||
if (style.getFill() && style.getFill().getColor()) {
|
||||
return style.getFill().getColor();
|
||||
}
|
||||
return '#888888';
|
||||
};
|
||||
|
||||
interface MapViewProps {
|
||||
|
|
@ -75,7 +69,6 @@ interface MapViewProps {
|
|||
const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) => {
|
||||
const navigate = useNavigate();
|
||||
|
||||
// -- STATE --
|
||||
const [isDarkMode, setIsDarkMode] = useState(true);
|
||||
const currentTheme = isDarkMode ? themes.dark : themes.light;
|
||||
|
||||
|
|
@ -90,7 +83,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
size: { width: 0, height: 0 }
|
||||
});
|
||||
|
||||
// -- REFS --
|
||||
const mapDivRef = useRef<HTMLDivElement>(null);
|
||||
const mapRef = useRef<Map | null>(null);
|
||||
const tileLayerRef = useRef<TileLayer<XYZ> | null>(null);
|
||||
|
|
@ -100,10 +92,8 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
);
|
||||
const uiContainerRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// -- GSAP ENTRANCE --
|
||||
useLayoutEffect(() => {
|
||||
const ctx = gsap.context(() => {
|
||||
// We use clearProps: 'all' to ensure GSAP doesn't leave the elements with weird opacity states
|
||||
gsap.from('.floating-ui', {
|
||||
y: 20,
|
||||
opacity: 0,
|
||||
|
|
@ -111,13 +101,12 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
stagger: 0.1,
|
||||
ease: 'power3.out',
|
||||
delay: 0.5,
|
||||
clearProps: 'opacity,transform' // Crucial: Ensures visibility after animation ends
|
||||
clearProps: 'opacity,transform'
|
||||
});
|
||||
}, uiContainerRef);
|
||||
return () => ctx.revert();
|
||||
}, []);
|
||||
|
||||
// -- MAP INIT --
|
||||
useEffect(() => {
|
||||
if (!mapRef.current) {
|
||||
const baseLayer = new TileLayer({
|
||||
|
|
@ -154,7 +143,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
}
|
||||
}, []);
|
||||
|
||||
// -- THEME SWITCHING --
|
||||
useEffect(() => {
|
||||
if (mapRef.current && tileLayerRef.current) {
|
||||
tileLayerRef.current.setSource(new XYZ({
|
||||
|
|
@ -164,7 +152,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
}
|
||||
}, [isDarkMode]);
|
||||
|
||||
// -- SEARCH DATA --
|
||||
useEffect(() => {
|
||||
if (initialAddressData && initialAddressData.length > 0) {
|
||||
const lat = parseFloat(initialAddressData[0].lat);
|
||||
|
|
@ -196,7 +183,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
const clickedLonLat = clickedCoordinate ? toLonLat(clickedCoordinate) : null;
|
||||
const activeLayerCount = Object.values(layerVisibility).filter(Boolean).length;
|
||||
|
||||
// -- STYLES --
|
||||
const styles = {
|
||||
wrapper: {
|
||||
position: 'relative' as const, width: '100vw', height: '100vh',
|
||||
|
|
@ -207,7 +193,7 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
uiLayer: {
|
||||
position: 'absolute' as const, top: 0, left: 0, width: '100%', height: '100%',
|
||||
pointerEvents: 'none' as const,
|
||||
zIndex: 1000, // FORCE TOP LAYER
|
||||
zIndex: 1000,
|
||||
padding: '24px',
|
||||
display: 'flex', flexDirection: 'column' as const, justifyContent: 'space-between',
|
||||
},
|
||||
|
|
@ -216,7 +202,7 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
alignItems: 'center',
|
||||
pointerEvents: 'auto' as const,
|
||||
gap: '12px',
|
||||
width: '100%' // Ensure it spans width
|
||||
width: '100%'
|
||||
},
|
||||
glassPanel: {
|
||||
background: currentTheme.glass,
|
||||
|
|
@ -232,7 +218,7 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
backdropFilter: 'blur(20px)', border: `1px solid ${currentTheme.border}`,
|
||||
boxShadow: currentTheme.shadow,
|
||||
transition: 'all 0.3s ease',
|
||||
flexShrink: 0 // Prevent collapsing
|
||||
flexShrink: 0
|
||||
},
|
||||
actionButton: {
|
||||
width: '48px', height: '48px', borderRadius: '50%',
|
||||
|
|
@ -240,10 +226,10 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
background: currentTheme.glass,
|
||||
backdropFilter: 'blur(20px)', WebkitBackdropFilter: 'blur(20px)',
|
||||
border: `1px solid ${currentTheme.border}`,
|
||||
color: currentTheme.iconColor, // Explicit high contrast color
|
||||
boxShadow: currentTheme.shadow, // Force visibility
|
||||
color: currentTheme.iconColor,
|
||||
boxShadow: currentTheme.shadow,
|
||||
cursor: 'pointer', transition: 'all 0.2s',
|
||||
flexShrink: 0 // Prevent collapsing
|
||||
flexShrink: 0
|
||||
},
|
||||
controlsContainer: {
|
||||
marginTop: 'auto', maxWidth: '340px', maxHeight: '60vh',
|
||||
|
|
@ -346,19 +332,15 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
<div style={styles.wrapper}>
|
||||
<style>{scrollbarStyles}</style>
|
||||
|
||||
{/* 1. MAP LAYER */}
|
||||
<div
|
||||
ref={mapDivRef}
|
||||
style={{ width: '100%', height: '100%', transition: 'filter 0.5s' }}
|
||||
/>
|
||||
|
||||
{/* 2. UI LAYER */}
|
||||
<div ref={uiContainerRef} style={styles.uiLayer}>
|
||||
|
||||
{/* TOP BAR */}
|
||||
<div style={styles.topBar}>
|
||||
|
||||
{/* Back Button */}
|
||||
<button
|
||||
className="floating-ui"
|
||||
style={styles.actionButton}
|
||||
|
|
@ -370,7 +352,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
<svg width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2"><path d="M19 12H5M12 19l-7-7 7-7"/></svg>
|
||||
</button>
|
||||
|
||||
{/* Location Badge */}
|
||||
<div className="floating-ui" style={styles.locationBadge}>
|
||||
<div style={{ width: '6px', height: '6px', background: '#00FF94', borderRadius: '50%', marginRight: '12px', boxShadow: '0 0 8px #00FF94' }} />
|
||||
<span style={{ fontSize: '14px', fontWeight: 600, letterSpacing: '0.5px', color: currentTheme.textMain, textTransform: 'uppercase' }}>
|
||||
|
|
@ -378,7 +359,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
</span>
|
||||
</div>
|
||||
|
||||
{/* Theme Toggle Button */}
|
||||
<button
|
||||
className="floating-ui"
|
||||
style={styles.actionButton}
|
||||
|
|
@ -396,7 +376,6 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
|
||||
</div>
|
||||
|
||||
{/* CONTROLS */}
|
||||
<div className="floating-ui" style={styles.controlsContainer}>
|
||||
<div style={styles.controlsHeader}>Overlay Controls</div>
|
||||
|
||||
|
|
@ -428,14 +407,12 @@ const MapView: React.FC<MapViewProps> = ({ initialAddressData, initialTerm }) =>
|
|||
</div>
|
||||
</div>
|
||||
|
||||
{/* HUD */}
|
||||
{clickedCoordinate && (
|
||||
<div className="floating-ui" style={styles.hud}>
|
||||
LOCATION_LOCK: {clickedCoordinate[1].toFixed(4)} N, {clickedCoordinate[0].toFixed(4)} E
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* RIGHT SIDEBAR */}
|
||||
<div className="floating-ui" style={styles.rightSidebar}>
|
||||
<div style={styles.sidebarCard}>
|
||||
<div style={styles.sidebarTitle}>General Values</div>
|
||||
|
|
|
|||
Loading…
Reference in a new issue