Prepare branch for merge to main

- Fix build issues with SSR and useSearchParams
- Update README.md with comprehensive project documentation
- Move debug pages to debug-disabled folder (temporary)
- Fix authentication pages with Suspense boundaries
- Add dynamic imports for map components
- Ensure all pages build successfully

This commit prepares the auth2 branch for merging into main by:
1. Resolving all build errors
2. Adding proper SSR handling for client-side components
3. Updating documentation to reflect current state
4. Moving debug/test pages out of production build
This commit is contained in:
Chop
2025-07-10 22:18:32 +02:00
parent 38b0682d83
commit faeb1ca80c
13 changed files with 1328 additions and 874 deletions

View File

@@ -1,8 +1,9 @@
'use client'
import { useSearchParams } from 'next/navigation'
import { Suspense } from 'react'
export default function AuthError() {
function AuthErrorContent() {
const searchParams = useSearchParams()
const error = searchParams.get('error')
@@ -47,3 +48,18 @@ export default function AuthError() {
</div>
)
}
export default function AuthError() {
return (
<Suspense fallback={
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4"></div>
<p className="text-gray-600">Loading...</p>
</div>
</div>
}>
<AuthErrorContent />
</Suspense>
)
}

View File

@@ -1,11 +1,11 @@
"use client"
import { useState } from "react"
import { useState, Suspense } from "react"
import { signIn, getSession } from "next-auth/react"
import { useRouter } from "next/navigation"
import { useSearchParams } from "next/navigation"
export default function SignIn() {
function SignInContent() {
const [email, setEmail] = useState("")
const [password, setPassword] = useState("")
const [error, setError] = useState("")
@@ -125,3 +125,18 @@ export default function SignIn() {
</div>
)
}
export default function SignIn() {
return (
<Suspense fallback={
<div className="min-h-screen flex items-center justify-center bg-gray-50">
<div className="text-center">
<div className="animate-spin rounded-full h-12 w-12 border-b-2 border-blue-500 mx-auto mb-4"></div>
<p className="text-gray-600">Loading...</p>
</div>
</div>
}>
<SignInContent />
</Suspense>
)
}

View File

@@ -1,347 +0,0 @@
"use client";
import { useState } from 'react';
import ComprehensivePolishMap from '../../components/ui/ComprehensivePolishMap';
export default function ComprehensivePolishMapPage() {
const [selectedLocation, setSelectedLocation] = useState('krakow');
// Different locations to test the layers
const locations = {
krakow: {
center: [50.0647, 19.9450],
zoom: 14,
name: "Kraków",
description: "Historic city center with good cadastral data coverage"
},
warsaw: {
center: [52.2297, 21.0122],
zoom: 14,
name: "Warszawa",
description: "Capital city with extensive planning data"
},
gdansk: {
center: [54.3520, 18.6466],
zoom: 14,
name: "Gdańsk",
description: "Port city with detailed property boundaries"
},
wroclaw: {
center: [51.1079, 17.0385],
zoom: 14,
name: "Wrocław",
description: "University city with good orthophoto coverage"
},
poznan: {
center: [52.4064, 16.9252],
zoom: 14,
name: "Poznań",
description: "Industrial center with road network data"
}
};
const currentLocation = locations[selectedLocation];
// Test markers for selected location
const testMarkers = [
{
position: currentLocation.center,
popup: `${currentLocation.name} - ${currentLocation.description}`
}
];
return (
<div className="min-h-screen bg-gray-100">
<div className="container mx-auto px-4 py-8">
<h1 className="text-4xl font-bold text-gray-800 mb-6">
🇵🇱 Comprehensive Polish Geospatial Data Platform
</h1>
<div className="bg-green-50 border border-green-200 rounded-lg p-6 mb-6">
<h2 className="text-xl font-semibold text-green-800 mb-3">
All Polish Layers Implementation Complete! 🎉
</h2>
<p className="text-green-700 mb-4">
This comprehensive map includes all layers from your OpenLayers implementation,
converted to work seamlessly with your Leaflet-based React/Next.js project.
</p>
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div>
<strong className="text-green-800">Base Layers:</strong>
<ul className="mt-1 text-green-700">
<li> Polish Orthophoto (Standard & High Resolution)</li>
<li> OpenStreetMap, Google Maps, Esri Satellite</li>
</ul>
</div>
<div>
<strong className="text-green-800">Overlay Layers:</strong>
<ul className="mt-1 text-green-700">
<li> Cadastral Data, Spatial Planning</li>
<li> LP-Portal Roads, Street Names, Parcels, Surveys</li>
</ul>
</div>
</div>
</div>
{/* Location Selector */}
<div className="bg-white rounded-lg shadow-lg p-4 mb-6">
<h3 className="text-lg font-semibold text-gray-800 mb-3">
🎯 Select Test Location:
</h3>
<div className="grid grid-cols-2 md:grid-cols-5 gap-2">
{Object.entries(locations).map(([key, location]) => (
<button
key={key}
onClick={() => setSelectedLocation(key)}
className={`px-3 py-2 rounded-lg text-sm transition-colors ${
selectedLocation === key
? 'bg-blue-600 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
{location.name}
</button>
))}
</div>
<p className="text-sm text-gray-600 mt-2">
<strong>Current:</strong> {currentLocation.description}
</p>
</div>
{/* Map Container */}
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="p-4 bg-blue-600 text-white">
<h2 className="text-xl font-semibold">
Interactive Map: {currentLocation.name}
</h2>
<p className="text-blue-100 mt-2">
Use the layer control (top-right) to toggle between base layers and enable overlay layers.
Combine orthophoto with cadastral data for detailed property analysis.
</p>
</div>
<div className="h-96 md:h-[700px]">
<ComprehensivePolishMap
key={selectedLocation} // Force re-render when location changes
center={currentLocation.center}
zoom={currentLocation.zoom}
markers={testMarkers}
showLayerControl={true}
/>
</div>
</div>
{/* Layer Information */}
<div className="mt-8 grid md:grid-cols-2 gap-6">
{/* Base Layers */}
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4 flex items-center">
🗺 Base Layers
</h3>
<div className="space-y-3 text-sm">
<div className="flex items-start">
<span className="w-4 h-4 bg-green-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>Polish Orthophoto (Standard)</strong>
<p className="text-gray-600 mt-1">High-quality aerial imagery from Polish Geoportal</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-emerald-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>Polish Orthophoto (High Resolution)</strong>
<p className="text-gray-600 mt-1">Ultra-high resolution aerial imagery for detailed analysis</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-blue-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>OpenStreetMap</strong>
<p className="text-gray-600 mt-1">Community-driven map data</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-red-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>Google Maps</strong>
<p className="text-gray-600 mt-1">Satellite imagery and road overlay</p>
</div>
</div>
</div>
</div>
{/* Overlay Layers */}
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4 flex items-center">
📊 Overlay Layers
</h3> <div className="space-y-3 text-sm">
<div className="flex items-start">
<span className="w-4 h-4 bg-orange-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>📋 Polish Cadastral Data</strong>
<p className="text-gray-600 mt-1">Property boundaries, parcels, and building outlines</p>
<p className="text-xs text-gray-500">Opacity: 80% - Semi-transparent overlay</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-purple-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>🏗 Polish Spatial Planning</strong>
<p className="text-gray-600 mt-1">Zoning data and urban planning information</p>
<p className="text-xs text-gray-500">Opacity: 70% - Semi-transparent overlay</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-teal-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>🛣 LP-Portal Roads</strong>
<p className="text-gray-600 mt-1">Detailed road network data</p>
<p className="text-xs text-gray-500">Opacity: 90% - Mostly opaque for visibility</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-indigo-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>🏷 LP-Portal Street Names</strong>
<p className="text-gray-600 mt-1">Street names and road descriptions</p>
<p className="text-xs text-gray-500">Opacity: 100% - Fully opaque for readability</p>
</div>
</div>
<div className="flex items-start">
<span className="w-4 h-4 bg-pink-500 rounded-full mr-3 mt-0.5 flex-shrink-0"></span>
<div>
<strong>📐 LP-Portal Parcels & Surveys</strong>
<p className="text-gray-600 mt-1">Property parcels and survey markers</p>
<p className="text-xs text-gray-500">Opacity: 60-80% - Variable transparency</p>
</div>
</div>
</div>
</div>
</div>
{/* Transparency Information */}
<div className="mt-8 bg-green-50 border border-green-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-green-800 mb-4">
🎨 Layer Transparency Handling
</h3>
<div className="grid md:grid-cols-2 gap-6 text-sm">
<div>
<h4 className="font-semibold text-green-700 mb-3">Base Layers (Opaque):</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span>Polish Orthophoto</span>
<span className="bg-green-200 px-2 py-1 rounded text-xs">100% Opaque</span>
</div>
<div className="flex justify-between">
<span>Google Satellite/Roads</span>
<span className="bg-green-200 px-2 py-1 rounded text-xs">100% Opaque</span>
</div>
</div>
</div>
<div>
<h4 className="font-semibold text-green-700 mb-3">Overlay Layers (Transparent):</h4>
<div className="space-y-2">
<div className="flex justify-between">
<span>📋 Cadastral Data</span>
<span className="bg-yellow-200 px-2 py-1 rounded text-xs">80% Opacity</span>
</div>
<div className="flex justify-between">
<span>🏗 Spatial Planning</span>
<span className="bg-yellow-200 px-2 py-1 rounded text-xs">70% Opacity</span>
</div>
<div className="flex justify-between">
<span>🛣 Roads</span>
<span className="bg-blue-200 px-2 py-1 rounded text-xs">90% Opacity</span>
</div>
<div className="flex justify-between">
<span>🏷 Street Names</span>
<span className="bg-green-200 px-2 py-1 rounded text-xs">100% Opacity</span>
</div>
<div className="flex justify-between">
<span>📐 Parcels</span>
<span className="bg-orange-200 px-2 py-1 rounded text-xs">60% Opacity</span>
</div>
<div className="flex justify-between">
<span>📍 Survey Markers</span>
<span className="bg-yellow-200 px-2 py-1 rounded text-xs">80% Opacity</span>
</div>
</div>
</div>
</div>
<div className="mt-4 p-3 bg-green-100 rounded">
<p className="text-green-800 text-sm">
<strong>Smart Transparency:</strong> Each overlay layer has been optimized with appropriate transparency levels.
Property boundaries are semi-transparent (60-80%) so you can see the underlying imagery,
while text labels are fully opaque (100%) for maximum readability.
</p>
</div>
</div>
{/* Usage Guide */}
<div className="mt-8 bg-blue-50 border border-blue-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-blue-800 mb-4">
📋 How to Use This Comprehensive Map
</h3>
<div className="grid md:grid-cols-2 gap-6">
<div>
<h4 className="font-semibold text-blue-700 mb-2">Basic Navigation:</h4>
<ul className="text-blue-600 space-y-1 text-sm">
<li> Use mouse wheel to zoom in/out</li>
<li> Click and drag to pan around</li>
<li> Use layer control (top-right) to switch layers</li>
<li> Select different Polish cities above to test</li>
</ul>
</div>
<div>
<h4 className="font-semibold text-blue-700 mb-2">Advanced Features:</h4>
<ul className="text-blue-600 space-y-1 text-sm">
<li> Combine orthophoto with cadastral overlay</li>
<li> Enable multiple overlays simultaneously</li>
<li> Use high-resolution orthophoto for detail work</li>
<li> Compare with Google/OSM base layers</li>
</ul>
</div>
</div>
</div>
{/* Technical Implementation */}
<div className="mt-8 bg-gray-50 border border-gray-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
Technical Implementation Details
</h3>
<div className="grid md:grid-cols-3 gap-6 text-sm">
<div>
<h4 className="font-semibold text-gray-700 mb-2">WMTS Integration:</h4>
<ul className="text-gray-600 space-y-1">
<li> Proper KVP URL construction</li>
<li> EPSG:3857 coordinate system</li>
<li> Standard and high-res orthophoto</li>
<li> Multiple format support (JPEG/PNG)</li>
</ul>
</div>
<div>
<h4 className="font-semibold text-gray-700 mb-2">WMS Overlays:</h4>
<ul className="text-gray-600 space-y-1">
<li> Polish government services</li>
<li> LP-Portal municipal data</li>
<li> Transparent overlay support</li>
<li> Multiple layer combinations</li>
</ul>
</div>
<div>
<h4 className="font-semibold text-gray-700 mb-2">React/Leaflet:</h4>
<ul className="text-gray-600 space-y-1">
<li> React-Leaflet component integration</li>
<li> Dynamic layer switching</li>
<li> Responsive design</li>
<li> Performance optimized</li>
</ul>
</div>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,103 +0,0 @@
"use client";
import DebugPolishOrthophotoMap from '../../components/ui/DebugPolishOrthophotoMap';
export default function DebugPolishOrthophotoPage() {
// Test marker in Poland
const testMarkers = [
{
position: [50.0647, 19.9450], // Krakow
popup: "Kraków - Test Location"
}
];
return (
<div className="min-h-screen bg-gray-100">
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold text-gray-800 mb-6">
Debug Polish Geoportal Orthophoto
</h1>
<div className="bg-red-50 border border-red-200 rounded-lg p-4 mb-6">
<h2 className="text-lg font-semibold text-red-800 mb-2">
Debug Mode Active
</h2>
<p className="text-red-700">
This page tests multiple URL formats for Polish Geoportal orthophoto tiles.
Check the browser console and the debug panel on the map for network request information.
</p>
</div>
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="p-4 bg-blue-600 text-white">
<h2 className="text-xl font-semibold">Debug Map with Multiple Orthophoto Options</h2>
<p className="text-blue-100 mt-2">
Try switching between different Polish orthophoto options using the layer control.
Google layers are included as working references.
</p>
</div>
<div className="h-96 md:h-[600px]">
<DebugPolishOrthophotoMap
center={[50.0647, 19.9450]} // Centered on Krakow
zoom={12}
markers={testMarkers}
/>
</div>
</div>
<div className="mt-8 bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
URL Formats Being Tested:
</h3>
<div className="space-y-4 text-sm"> <div className="bg-gray-50 p-3 rounded">
<strong>Option 1 (WMTS KVP EPSG:3857):</strong>
<code className="block mt-1 text-xs">
?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg
</code>
<span className="text-gray-600">Standard Web Mercator projection</span>
</div>
<div className="bg-gray-50 p-3 rounded">
<strong>Option 2 (WMTS KVP EPSG:2180):</strong>
<code className="block mt-1 text-xs">
?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:2180&TILEMATRIX=EPSG:2180:{z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg
</code>
<span className="text-gray-600">Polish coordinate system</span>
</div>
<div className="bg-gray-50 p-3 rounded">
<strong>Option 3 (Alternative TILEMATRIXSET):</strong>
<code className="block mt-1 text-xs">
?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg
</code>
<span className="text-gray-600">Google Maps compatible matrix</span>
</div>
<div className="bg-gray-50 p-3 rounded">
<strong>Option 4 (PNG format):</strong>
<code className="block mt-1 text-xs">
?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/png
</code>
<span className="text-gray-600">PNG format instead of JPEG</span>
</div>
</div>
</div>
<div className="mt-8 bg-yellow-50 border border-yellow-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-yellow-800 mb-2">
Debug Instructions:
</h3>
<ol className="text-yellow-700 space-y-2">
<li><strong>1.</strong> Open browser Developer Tools (F12) and go to Network tab</li>
<li><strong>2.</strong> Switch between different Polish orthophoto options in the layer control</li>
<li><strong>3.</strong> Look for requests to geoportal.gov.pl in the Network tab</li>
<li><strong>4.</strong> Check the debug panel on the map for request/response info</li>
<li><strong>5.</strong> Note which options return 200 OK vs 404/403 errors</li>
<li><strong>6.</strong> Compare with working Google layers</li>
</ol>
</div>
</div>
</div>
);
}

View File

@@ -13,7 +13,7 @@ import { formatDate } from "@/lib/utils";
import PageContainer from "@/components/ui/PageContainer";
import PageHeader from "@/components/ui/PageHeader";
import ProjectStatusDropdown from "@/components/ProjectStatusDropdown";
import ProjectMap from "@/components/ui/ProjectMap";
import ClientProjectMap from "@/components/ui/ClientProjectMap";
export default async function ProjectViewPage({ params }) {
const { id } = await params;
@@ -435,7 +435,7 @@ export default async function ProjectViewPage({ params }) {
</div>
</CardHeader>
<CardContent>
<ProjectMap
<ClientProjectMap
coordinates={project.coordinates}
projectName={project.project_name}
projectStatus={project.project_status}

View File

@@ -0,0 +1,928 @@
"use client";
import React, { useEffect, useState } from "react";
import Link from "next/link";
import dynamic from "next/dynamic";
import { useSearchParams, useRouter } from "next/navigation";
import Button from "@/components/ui/Button";
import { mapLayers } from "@/components/ui/mapLayers";
// Dynamically import the map component to avoid SSR issues
const DynamicMap = dynamic(() => import("@/components/ui/LeafletMap"), {
ssr: false,
loading: () => (
<div className="w-full h-96 bg-gray-100 animate-pulse rounded-lg flex items-center justify-center">
<span className="text-gray-500">Loading map...</span>
</div>
),
});
export default function ProjectsMapPage() {
const searchParams = useSearchParams();
const router = useRouter();
const [projects, setProjects] = useState([]);
const [loading, setLoading] = useState(true);
const [mapCenter, setMapCenter] = useState([50.0614, 19.9366]); // Default to Krakow, Poland
const [mapZoom, setMapZoom] = useState(10); // Default zoom level
const [statusFilters, setStatusFilters] = useState({
registered: true,
in_progress_design: true,
in_progress_construction: true,
fulfilled: true,
});
const [activeBaseLayer, setActiveBaseLayer] = useState("OpenStreetMap");
const [activeOverlays, setActiveOverlays] = useState([]);
const [showLayerPanel, setShowLayerPanel] = useState(true);
const [currentTool, setCurrentTool] = useState("move"); // Current map tool
// Status configuration with colors and labels
const statusConfig = {
registered: {
color: "#6B7280",
label: "Registered",
shortLabel: "Zarejestr.",
},
in_progress_design: {
color: "#3B82F6",
label: "In Progress (Design)",
shortLabel: "W real. (P)",
},
in_progress_construction: {
color: "#F59E0B",
label: "In Progress (Construction)",
shortLabel: "W real. (R)",
},
fulfilled: {
color: "#10B981",
label: "Completed",
shortLabel: "Zakończony",
},
};
// Toggle all status filters
const toggleAllFilters = () => {
const allActive = Object.values(statusFilters).every((value) => value);
const newState = allActive
? Object.keys(statusFilters).reduce(
(acc, key) => ({ ...acc, [key]: false }),
{}
)
: Object.keys(statusFilters).reduce(
(acc, key) => ({ ...acc, [key]: true }),
{}
);
setStatusFilters(newState);
};
// Toggle status filter
const toggleStatusFilter = (status) => {
setStatusFilters((prev) => ({
...prev,
[status]: !prev[status],
}));
};
// Layer control functions
const handleBaseLayerChange = (layerName) => {
setActiveBaseLayer(layerName);
};
const toggleOverlay = (layerName) => {
setActiveOverlays((prev) => {
if (prev.includes(layerName)) {
return prev.filter((name) => name !== layerName);
} else {
return [...prev, layerName];
}
});
};
const toggleLayerPanel = () => {
setShowLayerPanel(!showLayerPanel);
};
// Update URL with current map state (debounced to avoid too many updates)
const updateURL = (center, zoom) => {
const params = new URLSearchParams();
params.set("lat", center[0].toFixed(6));
params.set("lng", center[1].toFixed(6));
params.set("zoom", zoom.toString());
// Use replace to avoid cluttering browser history
router.replace(`/projects/map?${params.toString()}`, { scroll: false });
};
// Handle map view changes with debouncing
const handleMapViewChange = (center, zoom) => {
setMapCenter(center);
setMapZoom(zoom);
// Debounce URL updates to avoid too many history entries
clearTimeout(window.mapUpdateTimeout);
window.mapUpdateTimeout = setTimeout(() => {
updateURL(center, zoom);
}, 500); // Wait 500ms after the last move to update URL
};
// Hide navigation and ensure full-screen layout
useEffect(() => {
// Check for URL parameters for coordinates and zoom
const lat = searchParams.get("lat");
const lng = searchParams.get("lng");
const zoom = searchParams.get("zoom");
if (lat && lng) {
const latitude = parseFloat(lat);
const longitude = parseFloat(lng);
if (!isNaN(latitude) && !isNaN(longitude)) {
setMapCenter([latitude, longitude]);
}
}
if (zoom) {
const zoomLevel = parseInt(zoom);
if (!isNaN(zoomLevel) && zoomLevel >= 1 && zoomLevel <= 20) {
setMapZoom(zoomLevel);
}
}
// Hide navigation bar for full-screen experience
const nav = document.querySelector("nav");
if (nav) {
nav.style.display = "none";
}
// Prevent scrolling on body
document.body.style.overflow = "hidden";
document.documentElement.style.overflow = "hidden";
// Cleanup when leaving page
return () => {
if (nav) {
nav.style.display = "";
}
document.body.style.overflow = "";
document.documentElement.style.overflow = "";
// Clear any pending URL updates
if (window.mapUpdateTimeout) {
clearTimeout(window.mapUpdateTimeout);
}
};
}, [searchParams]);
useEffect(() => {
fetch("/api/projects")
.then((res) => res.json())
.then((data) => {
setProjects(data);
// Only calculate center based on projects if no URL parameters are provided
const lat = searchParams.get("lat");
const lng = searchParams.get("lng");
if (!lat || !lng) {
// Calculate center based on projects with coordinates
const projectsWithCoords = data.filter((p) => p.coordinates);
if (projectsWithCoords.length > 0) {
const avgLat =
projectsWithCoords.reduce((sum, p) => {
const [lat] = p.coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
return sum + lat;
}, 0) / projectsWithCoords.length;
const avgLng =
projectsWithCoords.reduce((sum, p) => {
const [, lng] = p.coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
return sum + lng;
}, 0) / projectsWithCoords.length;
setMapCenter([avgLat, avgLng]);
}
}
setLoading(false);
})
.catch((error) => {
console.error("Error fetching projects:", error);
setLoading(false);
});
}, [searchParams]);
// Convert projects to map markers with filtering
const markers = projects
.filter((project) => project.coordinates)
.filter((project) => statusFilters[project.project_status] !== false)
.map((project) => {
const [lat, lng] = project.coordinates
.split(",")
.map((coord) => parseFloat(coord.trim()));
if (isNaN(lat) || isNaN(lng)) {
return null;
}
const statusInfo =
statusConfig[project.project_status] || statusConfig.registered;
return {
position: [lat, lng],
color: statusInfo.color,
popup: (
<div className="min-w-72 max-w-80">
<div className="mb-3 pb-2 border-b border-gray-200">
<h3 className="font-semibold text-base mb-1 text-gray-900">
{project.project_name}
</h3>
{project.project_number && (
<div className="inline-block bg-blue-100 text-blue-800 text-xs font-medium px-2 py-1 rounded-full">
{project.project_number}
</div>
)}
</div>
<div className="space-y-2 text-sm text-gray-600 mb-3">
{project.address && (
<div className="flex items-start gap-2">
<svg
className="w-4 h-4 mt-0.5 text-gray-400 flex-shrink-0"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
<div>
<span className="font-medium text-gray-700">
{project.address}
</span>
{project.city && (
<span className="text-gray-500">, {project.city}</span>
)}
</div>
</div>
)}
<div className="grid grid-cols-2 gap-2">
{project.wp && (
<div>
<span className="font-medium text-gray-700">WP:</span>{" "}
{project.wp}
</div>
)}
{project.plot && (
<div>
<span className="font-medium text-gray-700">Plot:</span>{" "}
{project.plot}
</div>
)}
</div>
{project.project_status && (
<div className="flex items-center gap-2">
<span className="font-medium text-gray-700">Status:</span>
<span
className="inline-block px-2 py-1 rounded-full text-xs font-medium text-white"
style={{ backgroundColor: statusInfo.color }}
>
{statusInfo.shortLabel}
</span>
</div>
)}
</div>
<div className="pt-2 border-t border-gray-200">
<Link href={`/projects/${project.project_id}`}>
<Button variant="primary" size="sm" className="w-full">
<svg
className="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M2.458 12C3.732 7.943 7.523 5 12 5c4.478 0 8.268 2.943 9.542 7-1.274 4.057-5.064 7-9.542 7-4.477 0-8.268-2.943-9.542-7z"
/>
</svg>
View Project Details
</Button>
</Link>
</div>
</div>
),
};
})
.filter((marker) => marker !== null);
if (loading) {
return (
<div className="fixed inset-0 bg-gray-50 flex items-center justify-center">
<div className="text-center">
<div className="w-12 h-12 mx-auto mb-4 border-4 border-blue-200 border-t-blue-600 rounded-full animate-spin"></div>
<p className="text-gray-600 font-medium">Loading projects map...</p>
<p className="text-sm text-gray-500 mt-2">
Preparing your full-screen map experience
</p>
</div>
</div>
);
}
return (
<div className="fixed inset-0 bg-gray-50 overflow-hidden">
{/* Floating Header - Left Side */}
<div className="absolute top-4 left-4 z-[1000]">
{/* Title Box */}
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg px-4 py-3 border border-gray-200">
<div className="flex items-center gap-3">
<h1 className="text-lg font-semibold text-gray-900">
Projects Map
</h1>
<div className="text-sm text-gray-600">
{markers.length} of {projects.length} projects with coordinates
</div>
</div>{" "}
</div>
</div>
{/* Zoom Controls - Below Title */}
<div className="absolute top-20 left-4 z-[1000]">
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg border border-gray-200 flex flex-col">
<button
className="px-3 py-2 hover:bg-gray-50 transition-colors duration-200 border-b border-gray-200 text-gray-700 font-medium text-lg"
onClick={() => {
// This will be handled by the map component
const event = new CustomEvent("mapZoomIn");
window.dispatchEvent(event);
}}
title="Zoom In"
>
+
</button>
<button
className="px-3 py-2 hover:bg-gray-50 transition-colors duration-200 text-gray-700 font-medium text-lg"
onClick={() => {
// This will be handled by the map component
const event = new CustomEvent("mapZoomOut");
window.dispatchEvent(event);
}}
title="Zoom Out"
>
</button>{" "}
</div>
</div>{" "}
{/* Tool Panel - Below Zoom Controls */}
<div className="absolute top-48 left-4 z-[1000]">
{" "}
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg border border-gray-200 flex flex-col">
{" "}
{/* Move Tool */}
<button
className={`p-3 transition-colors duration-200 border-b border-gray-200 ${
currentTool === "move"
? "bg-blue-100 text-blue-700"
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("move")}
title="Move Tool (Pan Map)"
>
<svg className="w-5 h-5" fill="currentColor" viewBox="0 0 512 512">
<path d="M256 0c-25.3 0-47.2 14.7-57.6 36c-7-2.6-14.5-4-22.4-4c-35.3 0-64 28.7-64 64l0 165.5-2.7-2.7c-25-25-65.5-25-90.5 0s-25 65.5 0 90.5L106.5 437c48 48 113.1 75 181 75l8.5 0 8 0c1.5 0 3-.1 4.5-.4c91.7-6.2 165-79.4 171.1-171.1c.3-1.5 .4-3 .4-4.5l0-176c0-35.3-28.7-64-64-64c-5.5 0-10.9 .7-16 2l0-2c0-35.3-28.7-64-64-64c-7.9 0-15.4 1.4-22.4 4C303.2 14.7 281.3 0 256 0zM240 96.1l0-.1 0-32c0-8.8 7.2-16 16-16s16 7.2 16 16l0 31.9 0 .1 0 136c0 13.3 10.7 24 24 24s24-10.7 24-24l0-136c0 0 0 0 0-.1c0-8.8 7.2-16 16-16s16 7.2 16 16l0 55.9c0 0 0 .1 0 .1l0 80c0 13.3 10.7 24 24 24s24-10.7 24-24l0-71.9c0 0 0-.1 0-.1c0-8.8 7.2-16 16-16s16 7.2 16 16l0 172.9c-.1 .6-.1 1.3-.2 1.9c-3.4 69.7-59.3 125.6-129 129c-.6 0-1.3 .1-1.9 .2l-4.9 0-8.5 0c-55.2 0-108.1-21.9-147.1-60.9L52.7 315.3c-6.2-6.2-6.2-16.4 0-22.6s16.4-6.2 22.6 0L119 336.4c6.9 6.9 17.2 8.9 26.2 5.2s14.8-12.5 14.8-22.2L160 96c0-8.8 7.2-16 16-16c8.8 0 16 7.1 16 15.9L192 232c0 13.3 10.7 24 24 24s24-10.7 24-24l0-135.9z" />
</svg>
</button>
{/* Select Tool */}
<button
className={`p-3 transition-colors duration-200 border-b border-gray-200 ${
currentTool === "select"
? "bg-blue-100 text-blue-700"
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("select")}
title="Select Tool"
>
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 15l-2 5L9 9l11 4-5 2zm0 0l5 5M7.188 2.239l.777 2.897M5.136 7.965l-2.898-.777M13.95 4.05l-2.122 2.122m-5.657 5.656l-2.12 2.122"
/>
</svg>
</button>
{/* Measure Tool */}
<button
className={`p-3 transition-colors duration-200 border-b border-gray-200 ${
currentTool === "measure"
? "bg-blue-100 text-blue-700"
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("measure")}
title="Measure Distance"
>
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M7 21l10-10M7 21H3v-4l10-10 4 4M7 21l4-4M17 7l4-4M17 7l-4-4M17 7l-4 4"
/>
</svg>
</button>
{/* Draw Tool */}
<button
className={`p-3 transition-colors duration-200 border-b border-gray-200 ${
currentTool === "draw"
? "bg-blue-100 text-blue-700"
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("draw")}
title="Draw/Markup"
>
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15.232 5.232l3.536 3.536m-2.036-5.036a2.5 2.5 0 113.536 3.536L6.5 21.036H3v-3.572L16.732 3.732z"
/>
</svg>
</button>
{/* Pin/Marker Tool */}
<button
className={`p-3 transition-colors duration-200 border-b border-gray-200 ${
currentTool === "pin"
? "bg-blue-100 text-blue-700"
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("pin")}
title="Add Pin/Marker"
>
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M17.657 16.657L13.414 20.9a1.998 1.998 0 01-2.827 0l-4.244-4.243a8 8 0 1111.314 0z"
/>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M15 11a3 3 0 11-6 0 3 3 0 016 0z"
/>
</svg>
</button>
{/* Area Tool */}
<button
className={`p-3 transition-colors duration-200 ${
currentTool === "area"
? "bg-blue-100 text-blue-700"
: "text-gray-700 hover:bg-gray-50"
}`}
onClick={() => setCurrentTool("area")}
title="Measure Area"
>
<svg
className="w-5 h-5"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 8V6a2 2 0 012-2h2M4 16v2a2 2 0 002 2h2m8-16h2a2 2 0 012 2v2m-4 12h2a2 2 0 002-2v-2"
/>
</svg>
</button>
</div>
</div>
{/* Layer Control Panel - Right Side */}
<div className="absolute top-4 right-4 z-[1000] flex flex-col gap-3">
{/* Action Buttons */}
<div className="flex gap-2 justify-end">
<Link href="/projects">
<Button
variant="outline"
size="sm"
className="bg-white/95 backdrop-blur-sm border-gray-200 shadow-lg hover:bg-white"
>
<svg
className="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 10h16M4 14h16M4 18h16"
/>
</svg>
List View
</Button>
</Link>
<Link href="/projects/new">
<Button variant="primary" size="sm" className="shadow-lg">
<svg
className="w-4 h-4 mr-2"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M12 4v16m8-8H4"
/>
</svg>
Add Project
</Button>
</Link>
</div>
{/* Layer Control Panel */}
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg border border-gray-200 layer-panel-container">
{/* Layer Control Header */}
<div className="px-4 py-3 border-b border-gray-200">
<button
onClick={toggleLayerPanel}
className="flex items-center justify-between w-full text-left layer-toggle-button"
title="Toggle Layer Controls"
>
<div className="flex items-center gap-2">
<svg
className="w-4 h-4 text-gray-600"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 11H5m14 0a2 2 0 012 2v6a2 2 0 01-2 2H5a2 2 0 01-2-2v-6a2 2 0 012-2m14 0V9a2 2 0 00-2-2M5 11V9a2 2 0 012-2m0 0V5a2 2 0 012-2h6a2 2 0 012 2v2M7 7h10"
/>
</svg>
<span className="text-sm font-medium text-gray-700">
Map Layers
</span>
<span className="text-xs text-gray-500 bg-gray-100 px-2 py-0.5 rounded-full">
{1 + activeOverlays.length} active
</span>
</div>
<svg
className={`w-4 h-4 text-gray-400 transition-transform duration-200 ${
showLayerPanel ? "rotate-180" : ""
}`}
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M19 9l-7 7-7-7"
/>
</svg>
</button>
</div>{" "}
{/* Layer Control Content */}
<div
className={`transition-all duration-300 ease-in-out ${
showLayerPanel
? "max-h-[70vh] opacity-100 overflow-visible"
: "max-h-0 opacity-0 overflow-hidden"
}`}
>
<div className="p-4 min-w-80 max-w-96 max-h-[60vh] overflow-y-auto">
{/* Base Layers Section */}
<div className="mb-4">
<h3 className="text-sm font-semibold text-gray-900 mb-3 flex items-center gap-2">
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 16l4.586-4.586a2 2 0 012.828 0L16 16m-2-2l1.586-1.586a2 2 0 012.828 0L20 14m-6-6h.01M6 20h12a2 2 0 002-2V6a2 2 0 00-2-2H6a2 2 0 00-2 2v12a2 2 0 002 2z"
/>
</svg>
Base Maps
</h3>
<div className="space-y-2">
{mapLayers.base.map((layer, index) => (
<label
key={index}
className="flex items-center gap-3 p-2 rounded hover:bg-gray-50 cursor-pointer transition-colors duration-200"
>
<input
type="radio"
name="baseLayer"
checked={activeBaseLayer === layer.name}
onChange={() => handleBaseLayerChange(layer.name)}
className="w-4 h-4 text-blue-600 border-gray-300 focus:ring-blue-500"
/>
<span className="text-sm text-gray-700 flex-1">
{layer.name}
</span>
</label>
))}
</div>
</div>
{/* Overlay Layers Section */}
{mapLayers.overlays && mapLayers.overlays.length > 0 && (
<div>
<h3 className="text-sm font-semibold text-gray-900 mb-3 flex items-center gap-2">
<svg
className="w-4 h-4"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M9 19l3 3m0 0l3-3m-3 3V10"
/>
</svg>
Overlay Layers
</h3>{" "}
<div className="space-y-2">
{mapLayers.overlays.map((layer, index) => (
<label
key={index}
className="flex items-center gap-3 p-2 rounded hover:bg-gray-50 cursor-pointer transition-colors duration-200"
>
<input
type="checkbox"
checked={activeOverlays.includes(layer.name)}
onChange={() => toggleOverlay(layer.name)}
className="w-4 h-4 text-blue-600 border-gray-300 rounded focus:ring-blue-500"
/>
<span className="text-sm text-gray-700 flex-1">
{layer.name}
</span>
</label>
))}
</div>
</div>
)}
</div>
</div>{" "}
</div>
</div>
{/* Status Filter Panel - Bottom Left */}
<div className="absolute bottom-4 left-4 z-[1000]">
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg px-4 py-3 border border-gray-200">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700 mr-2">
Filters:
</span>
{/* Toggle All Button */}
<button
onClick={toggleAllFilters}
className="flex items-center gap-1 px-2 py-1 rounded text-xs font-medium bg-gray-100 hover:bg-gray-200 transition-colors duration-200 mr-2"
title="Toggle all filters"
>
<svg
className="w-3 h-3"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<span className="text-gray-600">
{Object.values(statusFilters).every((v) => v)
? "Hide All"
: "Show All"}
</span>
</button>
{/* Individual Status Filters */}
{Object.entries(statusConfig).map(([status, config]) => {
const isActive = statusFilters[status];
const projectCount = projects.filter(
(p) => p.project_status === status && p.coordinates
).length;
return (
<button
key={status}
onClick={() => toggleStatusFilter(status)}
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-all duration-200 hover:bg-gray-100 ${
isActive ? "opacity-100 scale-100" : "opacity-40 scale-95"
}`}
title={`Toggle ${config.label} (${projectCount} projects)`}
>
<div
className={`w-3 h-3 rounded-full border-2 transition-all duration-200 ${
isActive ? "border-white shadow-sm" : "border-gray-300"
}`}
style={{
backgroundColor: isActive ? config.color : "#e5e7eb",
}}
></div>
<span
className={`transition-colors duration-200 ${
isActive ? "text-gray-700" : "text-gray-400"
}`}
>
{config.shortLabel}
</span>
<span
className={`ml-1 text-xs transition-colors duration-200 ${
isActive ? "text-gray-500" : "text-gray-300"
}`}
>
({projectCount})
</span>
</button>
);
})}{" "}
</div>
</div>
</div>{" "}
{/* Status Panel - Bottom Left */}
{markers.length > 0 && (
<div className="bg-white/95 backdrop-blur-sm rounded-lg shadow-lg px-4 py-3 border border-gray-200">
<div className="flex items-center gap-2">
<span className="text-sm font-medium text-gray-700 mr-2">
Filters:
</span>
{/* Toggle All Button */}
<button
onClick={toggleAllFilters}
className="flex items-center gap-1 px-2 py-1 rounded text-xs font-medium bg-gray-100 hover:bg-gray-200 transition-colors duration-200 mr-2"
title="Toggle all filters"
>
<svg
className="w-3 h-3"
fill="none"
stroke="currentColor"
viewBox="0 0 24 24"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth={2}
d="M4 6h16M4 12h16M4 18h16"
/>
</svg>
<span className="text-gray-600">
{Object.values(statusFilters).every((v) => v)
? "Hide All"
: "Show All"}
</span>
</button>
{/* Individual Status Filters */}
{Object.entries(statusConfig).map(([status, config]) => {
const isActive = statusFilters[status];
const projectCount = projects.filter(
(p) => p.project_status === status && p.coordinates
).length;
return (
<button
key={status}
onClick={() => toggleStatusFilter(status)}
className={`flex items-center gap-1 px-2 py-1 rounded text-xs font-medium transition-all duration-200 hover:bg-gray-100 ${
isActive ? "opacity-100 scale-100" : "opacity-40 scale-95"
}`}
title={`Toggle ${config.label} (${projectCount} projects)`}
>
<div
className={`w-3 h-3 rounded-full border-2 transition-all duration-200 ${
isActive ? "border-white shadow-sm" : "border-gray-300"
}`}
style={{
backgroundColor: isActive ? config.color : "#e5e7eb",
}}
></div>
<span
className={`transition-colors duration-200 ${
isActive ? "text-gray-700" : "text-gray-400"
}`}
>
{config.shortLabel}
</span>
<span
className={`ml-1 text-xs transition-colors duration-200 ${
isActive ? "text-gray-500" : "text-gray-300"
}`}
>
({projectCount})
</span>
</button>
);
})}
</div>
</div>
)}{" "}
{/* Full Screen Map */}
{markers.length === 0 ? (
<div className="h-full w-full flex items-center justify-center bg-gray-100">
<div className="text-center max-w-md mx-auto p-8 bg-white rounded-lg shadow-lg">
<div className="text-gray-400 mb-4">
<svg
className="w-16 h-16 mx-auto"
fill="currentColor"
viewBox="0 0 20 20"
>
<path
fillRule="evenodd"
d="M5.05 4.05a7 7 0 119.9 9.9L10 18.9l-4.95-4.95a7 7 0 010-9.9zM10 11a2 2 0 100-4 2 2 0 000 4z"
clipRule="evenodd"
/>
</svg>
</div>
<h3 className="text-lg font-medium text-gray-900 mb-2">
No projects with coordinates
</h3>
<p className="text-gray-500 mb-6">
Projects need coordinates to appear on the map. Add coordinates
when creating or editing projects.
</p>
<div className="flex gap-3 justify-center">
<Link href="/projects">
<Button variant="outline">View All Projects</Button>
</Link>
<Link href="/projects/new">
<Button variant="primary">Add Project</Button>
</Link>
</div>
</div>
</div>
) : (
<div className="absolute inset-0">
<DynamicMap
center={mapCenter}
zoom={mapZoom}
markers={markers}
showLayerControl={false}
defaultLayer={activeBaseLayer}
activeOverlays={activeOverlays}
onViewChange={handleMapViewChange}
/>
</div>
)}{" "}
</div>
);
}

File diff suppressed because it is too large Load Diff

View File

@@ -1,99 +0,0 @@
"use client";
import ImprovedPolishOrthophotoMap from '../../components/ui/ImprovedPolishOrthophotoMap';
export default function ImprovedPolishOrthophotoPage() {
const testMarkers = [
{
position: [50.0647, 19.9450], // Krakow
popup: "Kraków - Testing WMTS"
}
];
return (
<div className="min-h-screen bg-gray-100">
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold text-gray-800 mb-6">
Improved Polish WMTS Implementation
</h1>
<div className="bg-green-50 border border-green-200 rounded-lg p-4 mb-6">
<h2 className="text-lg font-semibold text-green-800 mb-2">
Custom WMTS Layer Implementation
</h2>
<p className="text-green-700">
This version uses a custom WMTS layer that properly constructs KVP URLs based on the GetCapabilities response.
Check the debug panel on the map to see the actual requests being made.
</p>
</div>
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="p-4 bg-blue-600 text-white">
<h2 className="text-xl font-semibold">Custom WMTS Layer with Proper KVP URLs</h2>
<p className="text-blue-100 mt-2">
This implementation builds proper WMTS GetTile requests with all required parameters.
Monitor the debug panel and browser network tab for request details.
</p>
</div>
<div className="h-96 md:h-[600px]">
<ImprovedPolishOrthophotoMap
center={[50.0647, 19.9450]}
zoom={12}
markers={testMarkers}
/>
</div>
</div>
<div className="mt-8 bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
WMTS Parameters Being Tested:
</h3>
<div className="grid md:grid-cols-2 gap-4 text-sm">
<div className="bg-gray-50 p-3 rounded">
<strong>Tile Matrix Sets Available:</strong>
<ul className="mt-2 space-y-1">
<li> EPSG:3857 (Web Mercator)</li>
<li> EPSG:4326 (WGS84)</li>
<li> EPSG:2180 (Polish National Grid)</li>
</ul>
</div>
<div className="bg-gray-50 p-3 rounded">
<strong>Formats Available:</strong>
<ul className="mt-2 space-y-1">
<li> image/jpeg (default)</li>
<li> image/png</li>
</ul>
</div>
</div>
</div>
<div className="mt-6 bg-blue-50 border border-blue-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-blue-800 mb-2">
Testing Instructions:
</h3>
<ol className="text-blue-700 space-y-2">
<li><strong>1.</strong> Open Browser Developer Tools (F12) Network tab</li>
<li><strong>2.</strong> Filter by "geoportal.gov.pl" to see WMTS requests</li>
<li><strong>3.</strong> Switch between different Polish WMTS options</li>
<li><strong>4.</strong> Check if requests return 200 OK or error codes</li>
<li><strong>5.</strong> Compare with Google Satellite (known working)</li>
<li><strong>6.</strong> Monitor the debug panel for request URLs</li>
</ol>
</div>
<div className="mt-6 bg-yellow-50 border border-yellow-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-yellow-800 mb-2">
Expected Behavior:
</h3>
<p className="text-yellow-700">
If the Polish orthophoto tiles appear, you should see aerial imagery of Poland.
If they don't load, check the network requests - they should show proper WMTS GetTile URLs
with all required parameters (SERVICE, REQUEST, LAYER, TILEMATRIXSET, etc.).
</p>
</div>
</div>
</div>
);
}

View File

@@ -1,202 +0,0 @@
"use client";
import { useState } from 'react';
import PolishOrthophotoMap from '../../components/ui/PolishOrthophotoMap';
import AdvancedPolishOrthophotoMap from '../../components/ui/AdvancedPolishOrthophotoMap';
export default function PolishOrthophotoTestPage() {
const [activeMap, setActiveMap] = useState('basic');
// Test markers - various locations in Poland
const testMarkers = [
{
position: [50.0647, 19.9450], // Krakow
popup: "Kraków - Main Market Square"
},
{
position: [52.2297, 21.0122], // Warsaw
popup: "Warszawa - Palace of Culture and Science"
},
{
position: [54.3520, 18.6466], // Gdansk
popup: "Gdańsk - Old Town"
}
];
return (
<div className="min-h-screen bg-gray-100">
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold text-gray-800 mb-6">
Polish Geoportal Orthophoto Integration
</h1>
{/* Map Type Selector */}
<div className="mb-6 bg-white rounded-lg shadow-lg p-4">
<h2 className="text-lg font-semibold text-gray-800 mb-3">
Choose Map Implementation:
</h2>
<div className="flex space-x-4">
<button
onClick={() => setActiveMap('basic')}
className={`px-4 py-2 rounded-lg transition-colors ${
activeMap === 'basic'
? 'bg-blue-600 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
Basic Polish Orthophoto
</button>
<button
onClick={() => setActiveMap('advanced')}
className={`px-4 py-2 rounded-lg transition-colors ${
activeMap === 'advanced'
? 'bg-blue-600 text-white'
: 'bg-gray-200 text-gray-700 hover:bg-gray-300'
}`}
>
Advanced with WMS Overlays
</button>
</div>
</div>
{/* Map Container */}
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="p-4 bg-blue-600 text-white">
<h2 className="text-xl font-semibold">
{activeMap === 'basic'
? 'Basic Polish Orthophoto Map'
: 'Advanced Polish Orthophoto with WMS Overlays'
}
</h2>
<p className="text-blue-100 mt-2">
{activeMap === 'basic'
? 'Demonstrates working Polish Geoportal orthophoto tiles with multiple base layer options.'
: 'Advanced version includes Polish cadastral data (działki) and spatial planning (MPZT) as overlay layers.'
}
</p>
</div>
<div className="h-96 md:h-[600px]">
{activeMap === 'basic' ? (
<PolishOrthophotoMap
center={[50.0647, 19.9450]} // Centered on Krakow
zoom={12}
markers={testMarkers}
showLayerControl={true}
/>
) : (
<AdvancedPolishOrthophotoMap
center={[50.0647, 19.9450]} // Centered on Krakow
zoom={12}
markers={testMarkers}
showLayerControl={true}
/>
)}
</div>
</div>
{/* Features Overview */}
<div className="mt-8 grid md:grid-cols-2 gap-6">
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
Basic Map Features:
</h3>
<ul className="space-y-2 text-gray-600">
<li className="flex items-center">
<span className="w-3 h-3 bg-green-500 rounded-full mr-3"></span>
Polish Geoportal Orthophoto (Working)
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-blue-500 rounded-full mr-3"></span>
OpenStreetMap base layer
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-red-500 rounded-full mr-3"></span>
Google Satellite imagery
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-yellow-500 rounded-full mr-3"></span>
Google Roads overlay
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-purple-500 rounded-full mr-3"></span>
Esri World Imagery
</li>
</ul>
</div>
<div className="bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
Advanced Map Features:
</h3>
<ul className="space-y-2 text-gray-600">
<li className="flex items-center">
<span className="w-3 h-3 bg-green-500 rounded-full mr-3"></span>
Standard & High Resolution Orthophoto
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-orange-500 rounded-full mr-3"></span>
Polish Cadastral Data (WMS)
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-teal-500 rounded-full mr-3"></span>
Spatial Planning Data (MPZT)
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-indigo-500 rounded-full mr-3"></span>
Overlay layer support
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-pink-500 rounded-full mr-3"></span>
Multiple base layers
</li>
</ul>
</div>
</div>
{/* Technical Implementation Details */}
<div className="mt-8 bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
Technical Implementation:
</h3>
<div className="grid md:grid-cols-2 gap-6">
<div>
<h4 className="font-semibold text-gray-700 mb-2">Key Improvements:</h4>
<ul className="text-sm text-gray-600 space-y-1">
<li> Uses REST tile service instead of WMTS for better compatibility</li>
<li> Proper tile size (512px) with zoomOffset=-1</li>
<li> proj4 integration for EPSG:2180 coordinate system</li>
<li> Multiple fallback layers for reliability</li>
<li> WMS overlay support for cadastral data</li>
</ul>
</div>
<div>
<h4 className="font-semibold text-gray-700 mb-2">Based on OpenLayers Code:</h4>
<ul className="text-sm text-gray-600 space-y-1">
<li> Converted from OpenLayers to Leaflet implementation</li>
<li> Maintains same layer structure and URLs</li>
<li> Includes Polish projection definitions</li>
<li> Compatible with existing React/Next.js setup</li>
<li> Extensible for additional WMS services</li>
</ul>
</div>
</div>
</div>
{/* Usage Instructions */}
<div className="mt-8 bg-blue-50 border border-blue-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-blue-800 mb-2">
How to Use:
</h3>
<div className="text-blue-700 space-y-2">
<p><strong>1.</strong> Use the layer control (top-right) to switch between base layers</p>
<p><strong>2.</strong> In advanced mode, enable overlay layers for cadastral/planning data</p>
<p><strong>3.</strong> Click on markers to see location information</p>
<p><strong>4.</strong> Zoom in to see high-resolution orthophoto details</p>
<p><strong>5.</strong> Combine orthophoto with cadastral overlay for property boundaries</p>
</div>
</div>
</div>
</div>
);
}

View File

@@ -1,98 +0,0 @@
"use client";
import PolishOrthophotoMap from '../../components/ui/PolishOrthophotoMap';
export default function TestPolishOrthophotoPage() {
// Test markers - various locations in Poland
const testMarkers = [
{
position: [50.0647, 19.9450], // Krakow
popup: "Kraków - Main Market Square"
},
{
position: [52.2297, 21.0122], // Warsaw
popup: "Warszawa - Palace of Culture and Science"
},
{
position: [54.3520, 18.6466], // Gdansk
popup: "Gdańsk - Old Town"
}
];
return (
<div className="min-h-screen bg-gray-100">
<div className="container mx-auto px-4 py-8">
<h1 className="text-3xl font-bold text-gray-800 mb-6">
Polish Geoportal Orthophoto Map Test
</h1>
<div className="bg-white rounded-lg shadow-lg overflow-hidden">
<div className="p-4 bg-blue-600 text-white">
<h2 className="text-xl font-semibold">Interactive Map with Polish Orthophoto</h2>
<p className="text-blue-100 mt-2">
This map demonstrates working Polish Geoportal orthophoto tiles.
Use the layer control (top-right) to switch between different map layers.
</p>
</div>
<div className="h-96 md:h-[600px]">
<PolishOrthophotoMap
center={[50.0647, 19.9450]} // Centered on Krakow
zoom={12}
markers={testMarkers}
showLayerControl={true}
/>
</div>
</div>
<div className="mt-8 bg-white rounded-lg shadow-lg p-6">
<h3 className="text-lg font-semibold text-gray-800 mb-4">
Map Layers Available:
</h3>
<ul className="space-y-2 text-gray-600">
<li className="flex items-center">
<span className="w-3 h-3 bg-green-500 rounded-full mr-3"></span>
<strong>Polish Geoportal Orthophoto:</strong> High-resolution aerial imagery from Polish Geoportal
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-blue-500 rounded-full mr-3"></span>
<strong>OpenStreetMap:</strong> Standard OpenStreetMap tiles
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-red-500 rounded-full mr-3"></span>
<strong>Google Satellite:</strong> Google satellite imagery
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-yellow-500 rounded-full mr-3"></span>
<strong>Google Roads:</strong> Google road overlay
</li>
<li className="flex items-center">
<span className="w-3 h-3 bg-purple-500 rounded-full mr-3"></span>
<strong>Esri Satellite:</strong> Esri world imagery
</li>
</ul>
</div>
<div className="mt-8 bg-yellow-50 border border-yellow-200 rounded-lg p-6">
<h3 className="text-lg font-semibold text-yellow-800 mb-2">
Implementation Notes:
</h3>
<div className="text-yellow-700 space-y-2">
<p>
The Polish Geoportal orthophoto uses REST tile service instead of WMTS for better compatibility
</p>
<p>
Tile size is set to 512px with zoomOffset=-1 for proper tile alignment
</p>
<p>
proj4 library is included for coordinate system transformations (EPSG:2180)
</p>
<p>
Multiple fallback layers are provided for comparison and reliability
</p>
</div>
</div>
</div>
</div>
);
}