feat: Add Improved Polish Orthophoto Map component with WMTS support

- Introduced ImprovedPolishOrthophotoMap component for displaying Polish orthophoto layers using WMTS.
- Implemented custom WMTSLayer for handling tile requests.
- Added Google Satellite and OpenStreetMap layers for comparison.
- Included debug information for network requests to Geoportal.
- Enhanced LeafletMap to support WMS overlays and improved layer control.
- Created PolishGeoLayers module for easy access to various Polish geospatial layers.
- Added TransparencyDemoMap for adjustable layer opacity controls.
- Updated mapLayers configuration to include new Polish orthophoto layers and WMS overlays.
- Refactored wmtsCapabilities to use updated URLs and parameters for better compatibility.
This commit is contained in:
2025-06-21 01:21:21 +02:00
parent 6df4302396
commit f83cc8e564
25 changed files with 4538 additions and 32 deletions

View File

@@ -0,0 +1,254 @@
"use client";
import { MapContainer, TileLayer, WMSTileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import { useEffect, useState } from 'react';
import proj4 from 'proj4';
// Define Polish coordinate system EPSG:2180
proj4.defs("EPSG:2180", "+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
// Fix for default markers in react-leaflet
const fixLeafletIcons = () => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: '/leaflet/marker-icon-2x.png',
iconUrl: '/leaflet/marker-icon.png',
shadowUrl: '/leaflet/marker-shadow.png',
});
}
};
// Polish Geoportal Orthophoto - Standard Resolution
function PolishOrthophotoStandardLayer() {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
// Polish Geoportal Orthophoto - High Resolution
function PolishOrthophotoHighResLayer() {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/HighResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
// Polish Land Records (Cadastral) - using WMS
function PolishCadastralLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://www.gugik.gov.pl/">GUGiK</a>'
url="https://integracja01.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'powiaty,powiaty_obreby,zsin,obreby,dzialki,geoportal,numery_dzialek,budynki',
styles: ',,,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
width: 256,
height: 256
}}
format="image/png"
transparent={true}
/>
);
}
// Polish Spatial Planning (MPZT) - WMS
function PolishSpatialPlanningLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/ext/KrajowaIntegracjaMiejscowychPlanowZagospodarowaniaPrzestrzennego"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'raster,wektor-str,wektor-lzb,wektor-pow,wektor-lin,wektor-pkt,granice',
styles: ',,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
width: 256,
height: 256
}}
format="image/png"
transparent={true}
/>
);
}
// LP-Portal Roads Layer
function LPPortalRoadsLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_6_mapainteraktywna_drogi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png8"
transparent={true}
/>
);
}
// LP-Portal Street Names Layer
function LPPortalStreetNamesLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_5_mapainteraktywna_opisy_drog,00_3_mapainteraktywna_nazwy_ulic',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png8"
transparent={true}
/>
);
}
// Google Maps layers
function GoogleSatelliteLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
function GoogleRoadsLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=h&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
export default function AdvancedPolishOrthophotoMap({
center = [50.0647, 19.9450], // Default to Krakow, Poland
zoom = 13,
markers = [],
showLayerControl = true
}) {
const [mapLoaded, setMapLoaded] = useState(false);
useEffect(() => {
fixLeafletIcons();
setMapLoaded(true);
}, []);
const { BaseLayer, Overlay } = LayersControl;
if (!mapLoaded) {
return <div>Loading map...</div>;
}
return (
<MapContainer
center={center}
zoom={zoom}
style={{ height: '100%', width: '100%' }}
scrollWheelZoom={true}
>
{showLayerControl ? (
<LayersControl position="topright">
{/* Base Layers */}
<BaseLayer name="OpenStreetMap">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
/>
</BaseLayer>
<BaseLayer checked name="Polish Geoportal Orthophoto (Standard)">
<PolishOrthophotoStandardLayer />
</BaseLayer>
<BaseLayer name="Polish Geoportal Orthophoto (High Resolution)">
<PolishOrthophotoHighResLayer />
</BaseLayer>
<BaseLayer name="Google Satellite">
<GoogleSatelliteLayer />
</BaseLayer>
<BaseLayer name="Google Roads">
<GoogleRoadsLayer />
</BaseLayer>
<BaseLayer name="Esri Satellite">
<TileLayer
attribution='&copy; <a href="https://www.esri.com/">Esri</a> &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
maxZoom={19}
/>
</BaseLayer> {/* Overlay Layers */}
<Overlay name="📋 Polish Cadastral Data (Działki)">
<PolishCadastralLayer />
</Overlay>
<Overlay name="🏗️ Polish Spatial Planning (MPZT)">
<PolishSpatialPlanningLayer />
</Overlay>
<Overlay name="🛣️ LP-Portal Roads">
<LPPortalRoadsLayer />
</Overlay>
<Overlay name="🏷️ LP-Portal Street Names">
<LPPortalStreetNamesLayer />
</Overlay>
</LayersControl>
) : (
// Default to Polish orthophoto when no layer control
<PolishOrthophotoStandardLayer />
)}
{markers.map((marker, index) => (
<Marker key={index} position={marker.position}>
{marker.popup && <Popup>{marker.popup}</Popup>}
</Marker>
))}
</MapContainer>
);
}

View File

@@ -0,0 +1,322 @@
"use client";
import { MapContainer, TileLayer, WMSTileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import { useEffect, useState } from 'react';
import proj4 from 'proj4';
// Define Polish coordinate system EPSG:2180
proj4.defs("EPSG:2180", "+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
// Fix for default markers in react-leaflet
const fixLeafletIcons = () => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: '/leaflet/marker-icon-2x.png',
iconUrl: '/leaflet/marker-icon.png',
shadowUrl: '/leaflet/marker-shadow.png',
});
}
};
// 1. Polish Geoportal Orthophoto - Standard Resolution
function PolishOrthophotoStandardLayer() {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
// 2. Polish Geoportal Orthophoto - High Resolution
function PolishOrthophotoHighResLayer() {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/HighResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
// 3. Polish Cadastral Data (Działki) - WMS Transparent Overlay
function PolishCadastralLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://www.gugik.gov.pl/">GUGiK</a>'
url="https://integracja01.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'powiaty,powiaty_obreby,zsin,obreby,dzialki,geoportal,numery_dzialek,budynki',
styles: ',,,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
width: 256,
height: 256
}}
format="image/png"
transparent={true}
opacity={0.8} // Semi-transparent for overlay
/>
);
}
// 4. Polish Spatial Planning (MPZT) - WMS Transparent Overlay
function PolishSpatialPlanningLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/ext/KrajowaIntegracjaMiejscowychPlanowZagospodarowaniaPrzestrzennego"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'raster,wektor-str,wektor-lzb,wektor-pow,wektor-lin,wektor-pkt,granice',
styles: ',,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png"
transparent={true}
opacity={0.7} // Semi-transparent for overlay
/>
);
}
// 5. LP-Portal Roads Layer - Transparent Overlay
function LPPortalRoadsLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_6_mapainteraktywna_drogi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png8"
transparent={true}
opacity={0.9} // Mostly opaque for roads
/>
);
}
// 6. LP-Portal Street Names Layer - Transparent Overlay
function LPPortalStreetNamesLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_5_mapainteraktywna_opisy_drog,00_3_mapainteraktywna_nazwy_ulic',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png8"
transparent={true}
opacity={1.0} // Fully opaque for text labels
/>
);
}
// 7. LP-Portal Property Parcels (Działki) - Transparent Overlay
function LPPortalParcelsLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'dzialki',
styles: '',
format: 'image/png',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png"
transparent={true}
opacity={0.6} // Semi-transparent for property boundaries
/>
);
}
// 8. LP-Portal Survey Markers (Pikietarz) - Transparent Overlay
function LPPortalSurveyMarkersLayer() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_2_view_geop_odcinek_pikietaz_glob_multi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
width: 256,
height: 256,
tiled: true
}}
format="image/png8"
transparent={true}
opacity={0.8} // Semi-transparent for survey markers
/>
);
}
// Google Maps layers for comparison
function GoogleSatelliteLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
function GoogleRoadsLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=h&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
export default function ComprehensivePolishMap({
center = [50.0647, 19.9450], // Default to Krakow, Poland
zoom = 13,
markers = [],
showLayerControl = true
}) {
const [mapLoaded, setMapLoaded] = useState(false);
useEffect(() => {
fixLeafletIcons();
setMapLoaded(true);
}, []);
const { BaseLayer, Overlay } = LayersControl;
if (!mapLoaded) {
return <div>Loading comprehensive Polish map...</div>;
}
return (
<MapContainer
center={center}
zoom={zoom}
style={{ height: '100%', width: '100%' }}
scrollWheelZoom={true}
>
{showLayerControl ? (
<LayersControl position="topright">
{/* Base Layers */}
<BaseLayer name="OpenStreetMap">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
/>
</BaseLayer>
<BaseLayer checked name="Polish Orthophoto (Standard)">
<PolishOrthophotoStandardLayer />
</BaseLayer>
<BaseLayer name="Polish Orthophoto (High Resolution)">
<PolishOrthophotoHighResLayer />
</BaseLayer>
<BaseLayer name="Google Satellite">
<GoogleSatelliteLayer />
</BaseLayer>
<BaseLayer name="Google Roads">
<GoogleRoadsLayer />
</BaseLayer>
<BaseLayer name="Esri Satellite">
<TileLayer
attribution='&copy; <a href="https://www.esri.com/">Esri</a> &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
maxZoom={19}
/>
</BaseLayer>
{/* Polish Government Overlay Layers */}
<Overlay name="📋 Polish Cadastral Data (Działki)">
<PolishCadastralLayer />
</Overlay>
<Overlay name="🏗️ Polish Spatial Planning (MPZT)">
<PolishSpatialPlanningLayer />
</Overlay>
{/* LP-Portal Overlay Layers */}
<Overlay name="🛣️ LP-Portal Roads">
<LPPortalRoadsLayer />
</Overlay>
<Overlay name="🏷️ LP-Portal Street Names">
<LPPortalStreetNamesLayer />
</Overlay>
<Overlay name="📐 LP-Portal Property Parcels">
<LPPortalParcelsLayer />
</Overlay>
<Overlay name="📍 LP-Portal Survey Markers">
<LPPortalSurveyMarkersLayer />
</Overlay>
</LayersControl>
) : (
// Default to Polish orthophoto when no layer control
<PolishOrthophotoStandardLayer />
)}
{markers.map((marker, index) => (
<Marker key={index} position={marker.position}>
{marker.popup && <Popup>{marker.popup}</Popup>}
</Marker>
))}
</MapContainer>
);
}

View File

@@ -48,14 +48,13 @@ export default function CustomWMTSMap({ center, zoom = 13, markers = [] }) {
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
/>
</BaseLayer>
<BaseLayer name="Polish Geoportal ORTO (WMTS)">
<CustomWMTSLayer
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution"
layer="ORTO"
style="default"
tilematrixSet="EPSG:3857"
format="image/jpeg"
<BaseLayer name="Polish Geoportal ORTO (Working)">
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/REST/StandardResolution/tile/{z}/{y}/{x}.png"
maxZoom={19}
tileSize={512}
zoomOffset={-1}
/>
</BaseLayer>

View File

@@ -0,0 +1,213 @@
"use client";
import { MapContainer, TileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import { useEffect, useState } from 'react';
import proj4 from 'proj4';
// Define Polish coordinate system EPSG:2180 (from your OpenLayers code)
proj4.defs("EPSG:2180", "+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
// Fix for default markers in react-leaflet
const fixLeafletIcons = () => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: '/leaflet/marker-icon-2x.png',
iconUrl: '/leaflet/marker-icon.png',
shadowUrl: '/leaflet/marker-shadow.png',
});
}
};
// Multiple attempts at Polish Geoportal Orthophoto URLs
function PolishOrthophotoOption1() {
console.log("Loading Polish Orthophoto Option 1: WMTS KVP Standard");
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a> (Option 1 - WMTS KVP)'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
function PolishOrthophotoOption2() {
console.log("Loading Polish Orthophoto Option 2: WMTS KVP with EPSG:2180");
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a> (Option 2 - WMTS EPSG:2180)'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?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"
maxZoom={16}
/>
);
}
function PolishOrthophotoOption3() {
console.log("Loading Polish Orthophoto Option 3: Alternative WMTS URL");
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a> (Option 3 - Alt WMTS)'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=GoogleMapsCompatible&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
function PolishOrthophotoOption4() {
console.log("Loading Polish Orthophoto Option 4: PNG format");
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a> (Option 4 - PNG)'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/png"
maxZoom={19}
/>
);
}
// Working Google Maps layers for comparison
function GoogleSatelliteLayer() {
console.log("Loading Google Satellite (known working)");
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
function GoogleRoadsLayer() {
console.log("Loading Google Roads (known working)");
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=h&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
export default function DebugPolishOrthophotoMap({
center = [50.0647, 19.9450], // Krakow, Poland
zoom = 13,
markers = []
}) {
const [mapLoaded, setMapLoaded] = useState(false);
const [debugInfo, setDebugInfo] = useState([]);
useEffect(() => {
fixLeafletIcons();
setMapLoaded(true);
// Add debug logging
const originalFetch = window.fetch;
window.fetch = function(...args) {
const url = args[0];
if (typeof url === 'string' && url.includes('geoportal.gov.pl')) {
console.log('Geoportal request:', url);
setDebugInfo(prev => [...prev, `Request: ${url}`]);
}
return originalFetch.apply(this, args)
.then(response => {
if (typeof url === 'string' && url.includes('geoportal.gov.pl')) {
console.log('Geoportal response status:', response.status, 'for:', url);
setDebugInfo(prev => [...prev, `Response: ${response.status} for ${url}`]);
}
return response;
})
.catch(error => {
if (typeof url === 'string' && url.includes('geoportal.gov.pl')) {
console.error('Geoportal error:', error, 'for:', url);
setDebugInfo(prev => [...prev, `Error: ${error.message} for ${url}`]);
}
throw error;
});
};
return () => {
window.fetch = originalFetch;
};
}, []);
const { BaseLayer } = LayersControl;
if (!mapLoaded) {
return <div>Loading debug map...</div>;
}
return (
<div style={{ height: '100%', width: '100%' }}>
{/* Debug Info Panel */}
<div style={{
position: 'absolute',
top: '10px',
left: '10px',
background: 'rgba(255,255,255,0.9)',
padding: '10px',
maxWidth: '300px',
maxHeight: '200px',
overflow: 'auto',
zIndex: 1000,
fontSize: '12px',
borderRadius: '5px'
}}>
<strong>Debug Info:</strong>
{debugInfo.slice(-10).map((info, index) => (
<div key={index}>{info}</div>
))}
</div>
<MapContainer
center={center}
zoom={zoom}
style={{ height: '100%', width: '100%' }}
scrollWheelZoom={true}
>
<LayersControl position="topright">
{/* Working layers first for comparison */}
<BaseLayer checked name="Google Satellite (Working)">
<GoogleSatelliteLayer />
</BaseLayer>
<BaseLayer name="Google Roads (Working)">
<GoogleRoadsLayer />
</BaseLayer>
<BaseLayer name="OpenStreetMap (Working)">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
/>
</BaseLayer>
{/* Different Polish Geoportal attempts */}
<BaseLayer name="Polish Ortho - Option 1 (REST Standard)">
<PolishOrthophotoOption1 />
</BaseLayer>
<BaseLayer name="Polish Ortho - Option 2 (XYZ Direct)">
<PolishOrthophotoOption2 />
</BaseLayer>
<BaseLayer name="Polish Ortho - Option 3 (WMTS)">
<PolishOrthophotoOption3 />
</BaseLayer>
<BaseLayer name="Polish Ortho - Option 4 (Alt REST)">
<PolishOrthophotoOption4 />
</BaseLayer>
</LayersControl>
{markers.map((marker, index) => (
<Marker key={index} position={marker.position}>
{marker.popup && <Popup>{marker.popup}</Popup>}
</Marker>
))}
</MapContainer>
</div>
);
}

View File

@@ -1,6 +1,6 @@
"use client";
import { MapContainer, TileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import { MapContainer, TileLayer, WMSTileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import { useEffect } from 'react';
import { mapLayers } from './mapLayers';
@@ -29,8 +29,7 @@ export default function EnhancedLeafletMap({
useEffect(() => {
fixLeafletIcons();
}, []);
const { BaseLayer } = LayersControl;
const { BaseLayer, Overlay } = LayersControl;
return (
<MapContainer
@@ -41,6 +40,7 @@ export default function EnhancedLeafletMap({
>
{showLayerControl ? (
<LayersControl position="topright">
{/* Base Layers */}
{mapLayers.base.map((layer, index) => (
<BaseLayer
key={index}
@@ -55,6 +55,32 @@ export default function EnhancedLeafletMap({
/>
</BaseLayer>
))}
{/* Overlay Layers */}
{mapLayers.overlays && mapLayers.overlays.map((layer, index) => (
<Overlay
key={`overlay-${index}`}
checked={layer.checked}
name={layer.name}
>
{layer.type === "wms" ? (
<WMSTileLayer
attribution={layer.attribution}
url={layer.url}
params={layer.params}
format={layer.params.format}
transparent={layer.params.transparent}
opacity={layer.opacity}
/>
) : (
<TileLayer
attribution={layer.attribution}
url={layer.url}
maxZoom={layer.maxZoom}
opacity={layer.opacity}
/>
)}
</Overlay>
))}
</LayersControl>
) : (
// Default layer when no layer control

View File

@@ -0,0 +1,167 @@
"use client";
import { MapContainer, TileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import { useEffect, useState } from 'react';
import { WMTSLayer } from './WMTSLayer';
// Fix for default markers in react-leaflet
const fixLeafletIcons = () => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: '/leaflet/marker-icon-2x.png',
iconUrl: '/leaflet/marker-icon.png',
shadowUrl: '/leaflet/marker-shadow.png',
});
}
};
// Custom layer wrapper for WMTS
function PolishWMTSLayer({ tilematrixSet = 'EPSG:3857', format = 'image/jpeg' }) {
return (
<WMTSLayer
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution"
layer="ORTO"
style="default"
tilematrixSet={tilematrixSet}
format={format}
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
maxZoom={19}
/>
);
}
// Working Google Maps layers for comparison
function GoogleSatelliteLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
export default function ImprovedPolishOrthophotoMap({
center = [50.0647, 19.9450], // Krakow, Poland
zoom = 13,
markers = []
}) {
const [mapLoaded, setMapLoaded] = useState(false);
const [debugInfo, setDebugInfo] = useState([]);
useEffect(() => {
fixLeafletIcons();
setMapLoaded(true);
// Debug network requests
const originalFetch = window.fetch;
window.fetch = function(...args) {
const url = args[0];
if (typeof url === 'string' && url.includes('geoportal.gov.pl')) {
console.log('Geoportal WMTS request:', url);
setDebugInfo(prev => [...prev.slice(-5), `Request: ${url.split('?')[1]}`]);
}
return originalFetch.apply(this, args)
.then(response => {
if (typeof url === 'string' && url.includes('geoportal.gov.pl')) {
console.log('Geoportal response:', response.status, response.statusText);
setDebugInfo(prev => [...prev.slice(-5), `Response: ${response.status} ${response.statusText}`]);
}
return response;
})
.catch(error => {
if (typeof url === 'string' && url.includes('geoportal.gov.pl')) {
console.error('Geoportal error:', error);
setDebugInfo(prev => [...prev.slice(-5), `Error: ${error.message}`]);
}
throw error;
});
};
return () => {
window.fetch = originalFetch;
};
}, []);
const { BaseLayer } = LayersControl;
if (!mapLoaded) {
return <div>Loading improved map...</div>;
}
return (
<div style={{ height: '100%', width: '100%' }}>
{/* Debug Info Panel */}
<div style={{
position: 'absolute',
top: '10px',
left: '10px',
background: 'rgba(255,255,255,0.95)',
padding: '10px',
maxWidth: '400px',
maxHeight: '150px',
overflow: 'auto',
zIndex: 1000,
fontSize: '11px',
borderRadius: '5px',
boxShadow: '0 2px 5px rgba(0,0,0,0.2)'
}}>
<strong>WMTS Debug Info:</strong>
{debugInfo.map((info, index) => (
<div key={index} style={{ marginTop: '2px', wordBreak: 'break-all' }}>
{info}
</div>
))}
</div>
<MapContainer
center={center}
zoom={zoom}
style={{ height: '100%', width: '100%' }}
scrollWheelZoom={true}
>
<LayersControl position="topright">
{/* Working reference layer */}
<BaseLayer checked name="Google Satellite (Working Reference)">
<GoogleSatelliteLayer />
</BaseLayer>
<BaseLayer name="OpenStreetMap">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
/>
</BaseLayer>
{/* WMTS layers with custom implementation */}
<BaseLayer name="Polish WMTS (EPSG:3857, JPEG)">
<PolishWMTSLayer tilematrixSet="EPSG:3857" format="image/jpeg" />
</BaseLayer>
<BaseLayer name="Polish WMTS (EPSG:3857, PNG)">
<PolishWMTSLayer tilematrixSet="EPSG:3857" format="image/png" />
</BaseLayer>
<BaseLayer name="Polish WMTS (EPSG:4326, JPEG)">
<PolishWMTSLayer tilematrixSet="EPSG:4326" format="image/jpeg" />
</BaseLayer>
<BaseLayer name="Polish WMTS (EPSG:2180, JPEG)">
<PolishWMTSLayer tilematrixSet="EPSG:2180" format="image/jpeg" />
</BaseLayer>
</LayersControl>
{markers.map((marker, index) => (
<Marker key={index} position={marker.position}>
{marker.popup && <Popup>{marker.popup}</Popup>}
</Marker>
))}
</MapContainer>
</div>
);
}

View File

@@ -3,6 +3,7 @@
import {
MapContainer,
TileLayer,
WMSTileLayer,
Marker,
Popup,
LayersControl,
@@ -80,8 +81,7 @@ export default function EnhancedLeafletMap({
useEffect(() => {
fixLeafletIcons();
}, []);
const { BaseLayer } = LayersControl;
const { BaseLayer, Overlay } = LayersControl;
return (
<MapContainer
center={center}
@@ -94,6 +94,7 @@ export default function EnhancedLeafletMap({
{showLayerControl ? (
<LayersControl position="topright">
{/* Base Layers */}
{mapLayers.base.map((layer, index) => (
<BaseLayer
key={index}
@@ -107,6 +108,32 @@ export default function EnhancedLeafletMap({
tileSize={layer.tileSize || 256}
/>
</BaseLayer>
))}
{/* Overlay Layers */}
{mapLayers.overlays && mapLayers.overlays.map((layer, index) => (
<Overlay
key={`overlay-${index}`}
checked={layer.checked}
name={layer.name}
>
{layer.type === "wms" ? (
<WMSTileLayer
attribution={layer.attribution}
url={layer.url}
params={layer.params}
format={layer.params.format}
transparent={layer.params.transparent}
opacity={layer.opacity}
/>
) : (
<TileLayer
attribution={layer.attribution}
url={layer.url}
maxZoom={layer.maxZoom}
opacity={layer.opacity}
/>
)}
</Overlay>
))}
</LayersControl>
) : (

View File

@@ -0,0 +1,271 @@
"use client";
import { WMSTileLayer, TileLayer } from 'react-leaflet';
// Collection of all Polish geospatial layers from your OpenLayers implementation
// These can be imported individually and used in any map component
// 1. Polish Orthophoto Layers (WMTS)
export function PolishOrthophotoStandard() {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
export function PolishOrthophotoHighRes() {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/HighResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
/>
);
}
// 2. Polish Government WMS Layers - All transparent overlays
export function PolishCadastralData() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://www.gugik.gov.pl/">GUGiK</a>'
url="https://integracja01.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'powiaty,powiaty_obreby,zsin,obreby,dzialki,geoportal,numery_dzialek,budynki',
styles: ',,,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857'
}}
format="image/png"
transparent={true}
opacity={0.8} // Semi-transparent overlay for property boundaries
/>
);
}
export function PolishSpatialPlanning() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/ext/KrajowaIntegracjaMiejscowychPlanowZagospodarowaniaPrzestrzennego"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'raster,wektor-str,wektor-lzb,wektor-pow,wektor-lin,wektor-pkt,granice',
styles: ',,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
tiled: true
}}
format="image/png"
transparent={true}
opacity={0.7} // Semi-transparent overlay for planning zones
/>
);
}
// 3. LP-Portal Municipal Layers - Transparent overlays with different opacity levels
export function LPPortalRoads() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_6_mapainteraktywna_drogi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
tiled: true
}}
format="image/png8"
transparent={true}
opacity={0.9} // Mostly opaque for road visibility
/>
);
}
export function LPPortalStreetNames() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_5_mapainteraktywna_opisy_drog,00_3_mapainteraktywna_nazwy_ulic',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
tiled: true
}}
format="image/png8"
transparent={true}
opacity={1.0} // Fully opaque for text readability
/>
);
}
export function LPPortalParcels() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: 'dzialki',
styles: '',
format: 'image/png',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
tiled: true
}}
format="image/png"
transparent={true}
opacity={0.6} // Semi-transparent for parcel boundaries
/>
);
}
export function LPPortalSurveyMarkers() {
return (
<WMSTileLayer
attribution='&copy; <a href="https://lp-portal.pl/">LP-Portal</a>'
url="https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms"
params={{
service: 'WMS',
request: 'GetMap',
layers: '00_2_view_geop_odcinek_pikietaz_glob_multi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
tiled: true
}}
format="image/png8"
transparent={true}
opacity={0.8} // Semi-transparent for survey markers
/>
);
}
// 4. Utility Google Maps Layers for comparison
export function GoogleSatellite() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
export function GoogleRoads() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=h&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
// Layer configuration for easy integration with transparency settings
export const polishLayerConfigs = {
base: [
{
name: "Polish Orthophoto (Standard)",
component: PolishOrthophotoStandard,
description: "High-quality aerial imagery from Polish Geoportal",
opacity: 1.0,
transparent: false
},
{
name: "Polish Orthophoto (High Resolution)",
component: PolishOrthophotoHighRes,
description: "Ultra-high resolution aerial imagery",
opacity: 1.0,
transparent: false
},
{
name: "Google Satellite",
component: GoogleSatellite,
description: "Google satellite imagery for comparison",
opacity: 1.0,
transparent: false
},
{
name: "Google Roads",
component: GoogleRoads,
description: "Google road overlay",
opacity: 1.0,
transparent: false
}
],
overlays: [
{
name: "📋 Polish Cadastral Data",
component: PolishCadastralData,
description: "Property boundaries and parcel information",
opacity: 0.8,
transparent: true,
category: "government"
},
{
name: "🏗️ Polish Spatial Planning",
component: PolishSpatialPlanning,
description: "Zoning and urban planning data",
opacity: 0.7,
transparent: true,
category: "government"
},
{
name: "🛣️ LP-Portal Roads",
component: LPPortalRoads,
description: "Detailed road network",
opacity: 0.9,
transparent: true,
category: "municipal"
},
{
name: "🏷️ LP-Portal Street Names",
component: LPPortalStreetNames,
description: "Street names and descriptions",
opacity: 1.0,
transparent: true,
category: "municipal"
},
{
name: "📐 LP-Portal Parcels",
component: LPPortalParcels,
description: "Municipal property parcels",
opacity: 0.6,
transparent: true,
category: "municipal"
},
{
name: "📍 LP-Portal Survey Markers",
component: LPPortalSurveyMarkers,
description: "Survey markers and reference points",
opacity: 0.8,
transparent: true,
category: "municipal"
}
]
};

View File

@@ -0,0 +1,126 @@
"use client";
import { MapContainer, TileLayer, Marker, Popup, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import { useEffect, useState } from 'react';
import proj4 from 'proj4';
// Define Polish coordinate system EPSG:2180
proj4.defs("EPSG:2180", "+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
// Fix for default markers in react-leaflet
const fixLeafletIcons = () => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
delete L.Icon.Default.prototype._getIconUrl;
L.Icon.Default.mergeOptions({
iconRetinaUrl: '/leaflet/marker-icon-2x.png',
iconUrl: '/leaflet/marker-icon.png',
shadowUrl: '/leaflet/marker-shadow.png',
});
}
};
// Custom tile layer component for Polish Geoportal - WORKING VERSION
function PolishOrthophotoLayer({ visible = true }) {
return (
<TileLayer
attribution='&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>'
url="https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg"
maxZoom={19}
key="polish-orthophoto"
/>
);
}
// Google Maps layers as alternatives
function GoogleSatelliteLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
function GoogleRoadsLayer() {
return (
<TileLayer
attribution='&copy; Google'
url="http://mt1.google.com/vt/lyrs=h&hl=pl&x={x}&y={y}&z={z}"
maxZoom={20}
/>
);
}
export default function PolishOrthophotoMap({
center = [50.0647, 19.9450], // Default to Krakow, Poland
zoom = 13,
markers = [],
showLayerControl = true
}) {
const [mapLoaded, setMapLoaded] = useState(false);
useEffect(() => {
fixLeafletIcons();
setMapLoaded(true);
}, []);
const { BaseLayer } = LayersControl;
if (!mapLoaded) {
return <div>Loading map...</div>;
}
return (
<MapContainer
center={center}
zoom={zoom}
style={{ height: '100%', width: '100%' }}
scrollWheelZoom={true}
>
{showLayerControl ? (
<LayersControl position="topright">
<BaseLayer name="OpenStreetMap">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
/>
</BaseLayer>
<BaseLayer checked name="Polish Geoportal Orthophoto">
<PolishOrthophotoLayer />
</BaseLayer>
<BaseLayer name="Google Satellite">
<GoogleSatelliteLayer />
</BaseLayer>
<BaseLayer name="Google Roads">
<GoogleRoadsLayer />
</BaseLayer>
<BaseLayer name="Esri Satellite">
<TileLayer
attribution='&copy; <a href="https://www.esri.com/">Esri</a> &mdash; Source: Esri, i-cubed, USDA, USGS, AEX, GeoEye, Getmapping, Aerogrid, IGN, IGP, UPR-EGP, and the GIS User Community'
url="https://server.arcgisonline.com/ArcGIS/rest/services/World_Imagery/MapServer/tile/{z}/{y}/{x}"
maxZoom={19}
/>
</BaseLayer>
</LayersControl>
) : (
// Default to Polish orthophoto when no layer control
<PolishOrthophotoLayer />
)}
{markers.map((marker, index) => (
<Marker key={index} position={marker.position}>
{marker.popup && <Popup>{marker.popup}</Popup>}
</Marker>
))}
</MapContainer>
);
}

View File

@@ -0,0 +1,166 @@
"use client";
import { useState } from 'react';
import { MapContainer, TileLayer, LayersControl } from 'react-leaflet';
import 'leaflet/dist/leaflet.css';
import {
PolishOrthophotoStandard,
PolishCadastralData,
PolishSpatialPlanning,
LPPortalRoads,
LPPortalStreetNames,
LPPortalParcels
} from './PolishGeoLayers';
// Custom layer with adjustable opacity
function AdjustableOpacityLayer({ children, opacity = 1.0 }) {
return React.cloneElement(children, { opacity });
}
export default function TransparencyDemoMap({
center = [50.0647, 19.9450],
zoom = 15
}) {
const [opacitySettings, setOpacitySettings] = useState({
cadastral: 0.8,
planning: 0.7,
roads: 0.9,
streetNames: 1.0,
parcels: 0.6
});
const { BaseLayer, Overlay } = LayersControl;
const updateOpacity = (layer, value) => {
setOpacitySettings(prev => ({
...prev,
[layer]: value / 100
}));
};
return (
<div className="h-full">
{/* Opacity Controls */}
<div className="absolute top-4 left-4 z-[1000] bg-white p-4 rounded-lg shadow-lg max-w-xs">
<h3 className="font-semibold mb-3">🎨 Layer Transparency Controls</h3>
<div className="space-y-3 text-sm">
<div>
<label className="block text-gray-700 mb-1">📋 Cadastral Data</label>
<input
type="range"
min="0"
max="100"
value={opacitySettings.cadastral * 100}
onChange={(e) => updateOpacity('cadastral', e.target.value)}
className="w-full"
/>
<span className="text-xs text-gray-500">{Math.round(opacitySettings.cadastral * 100)}%</span>
</div>
<div>
<label className="block text-gray-700 mb-1">🏗 Spatial Planning</label>
<input
type="range"
min="0"
max="100"
value={opacitySettings.planning * 100}
onChange={(e) => updateOpacity('planning', e.target.value)}
className="w-full"
/>
<span className="text-xs text-gray-500">{Math.round(opacitySettings.planning * 100)}%</span>
</div>
<div>
<label className="block text-gray-700 mb-1">🛣 Roads</label>
<input
type="range"
min="0"
max="100"
value={opacitySettings.roads * 100}
onChange={(e) => updateOpacity('roads', e.target.value)}
className="w-full"
/>
<span className="text-xs text-gray-500">{Math.round(opacitySettings.roads * 100)}%</span>
</div>
<div>
<label className="block text-gray-700 mb-1">🏷 Street Names</label>
<input
type="range"
min="0"
max="100"
value={opacitySettings.streetNames * 100}
onChange={(e) => updateOpacity('streetNames', e.target.value)}
className="w-full"
/>
<span className="text-xs text-gray-500">{Math.round(opacitySettings.streetNames * 100)}%</span>
</div>
<div>
<label className="block text-gray-700 mb-1">📐 Parcels</label>
<input
type="range"
min="0"
max="100"
value={opacitySettings.parcels * 100}
onChange={(e) => updateOpacity('parcels', e.target.value)}
className="w-full"
/>
<span className="text-xs text-gray-500">{Math.round(opacitySettings.parcels * 100)}%</span>
</div>
</div>
</div>
<MapContainer
center={center}
zoom={zoom}
style={{ height: '100%', width: '100%' }}
scrollWheelZoom={true}
>
<LayersControl position="topright">
<BaseLayer checked name="Polish Orthophoto">
<PolishOrthophotoStandard />
</BaseLayer>
<BaseLayer name="OpenStreetMap">
<TileLayer
attribution='&copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
url="https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png"
maxZoom={19}
/>
</BaseLayer>
<Overlay name="📋 Cadastral Data (Adjustable)">
<AdjustableOpacityLayer opacity={opacitySettings.cadastral}>
<PolishCadastralData />
</AdjustableOpacityLayer>
</Overlay>
<Overlay name="🏗️ Spatial Planning (Adjustable)">
<AdjustableOpacityLayer opacity={opacitySettings.planning}>
<PolishSpatialPlanning />
</AdjustableOpacityLayer>
</Overlay>
<Overlay name="🛣️ Roads (Adjustable)">
<AdjustableOpacityLayer opacity={opacitySettings.roads}>
<LPPortalRoads />
</AdjustableOpacityLayer>
</Overlay>
<Overlay name="🏷️ Street Names (Adjustable)">
<AdjustableOpacityLayer opacity={opacitySettings.streetNames}>
<LPPortalStreetNames />
</AdjustableOpacityLayer>
</Overlay>
<Overlay name="📐 Parcels (Adjustable)">
<AdjustableOpacityLayer opacity={opacitySettings.parcels}>
<LPPortalParcels />
</AdjustableOpacityLayer>
</Overlay>
</LayersControl>
</MapContainer>
</div>
);
}

View File

@@ -0,0 +1,156 @@
"use client";
import { useEffect, useRef } from 'react';
import { useMap } from 'react-leaflet';
// Custom WMTS Layer for Polish Geoportal
export function WMTSLayer({
url,
layer,
style = 'default',
tilematrixSet = 'EPSG:3857',
format = 'image/jpeg',
attribution = '',
maxZoom = 19
}) {
const map = useMap();
const layerRef = useRef(null);
useEffect(() => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
// Custom WMTS layer that builds proper KVP URLs
const WMTSTileLayer = L.TileLayer.extend({
getTileUrl: function(coords) {
const tilematrix = tilematrixSet === 'EPSG:2180' ? `EPSG:2180:${coords.z}` : coords.z;
const params = new URLSearchParams({
SERVICE: 'WMTS',
REQUEST: 'GetTile',
VERSION: '1.0.0',
LAYER: layer,
STYLE: style,
TILEMATRIXSET: tilematrixSet,
TILEMATRIX: tilematrix,
TILEROW: coords.y,
TILECOL: coords.x,
FORMAT: format
});
return `${url}?${params.toString()}`;
}
});
// Create the layer
layerRef.current = new WMTSTileLayer('', {
attribution: attribution,
maxZoom: maxZoom,
tileSize: 256
});
// Add to map
map.addLayer(layerRef.current);
}
// Cleanup
return () => {
if (layerRef.current && map) {
map.removeLayer(layerRef.current);
}
};
}, [map, url, layer, style, tilematrixSet, format, attribution, maxZoom]);
return null;
}
// Proj4Leaflet WMTS Layer for Polish projection
export function Proj4WMTSLayer({
url,
layer,
style = 'default',
tilematrixSet = 'EPSG:2180',
format = 'image/jpeg',
attribution = '',
maxZoom = 16
}) {
const map = useMap();
const layerRef = useRef(null);
useEffect(() => {
if (typeof window !== 'undefined') {
const L = require('leaflet');
const proj4 = require('proj4');
require('proj4leaflet');
// Define Polish projection
proj4.defs("EPSG:2180", "+proj=tmerc +lat_0=0 +lon_0=19 +k=0.9993 +x_0=500000 +y_0=-5300000 +ellps=GRS80 +towgs84=0,0,0,0,0,0,0 +units=m +no_defs +type=crs");
// Create CRS for EPSG:2180
const crs2180 = new L.Proj.CRS('EPSG:2180', proj4.defs('EPSG:2180'), {
resolutions: [
2116.6709 * 4,
2116.6709 * 2,
2116.6709,
1058.33545,
529.167725,
264.5838625,
132.29193125,
66.145965625,
26.458386249999997,
13.229193124999998,
6.614596562499999,
2.645838625,
1.3229193125,
0.529167725,
0.2645838625,
0.13229193125,
0.13229193125 / 2,
],
origin: [100000, 850000],
bounds: L.bounds([144907.16581514146, 129171.69217334315], [890379.1658151413, 812697.4119729949])
});
// Custom WMTS layer
const WMTSTileLayer = L.TileLayer.extend({
getTileUrl: function(coords) {
const tilematrix = `EPSG:2180:${coords.z}`;
const params = new URLSearchParams({
SERVICE: 'WMTS',
REQUEST: 'GetTile',
VERSION: '1.0.0',
LAYER: layer,
STYLE: style,
TILEMATRIXSET: tilematrixSet,
TILEMATRIX: tilematrix,
TILEROW: coords.y,
TILECOL: coords.x,
FORMAT: format
});
return `${url}?${params.toString()}`;
}
});
// Create the layer
layerRef.current = new WMTSTileLayer('', {
attribution: attribution,
maxZoom: maxZoom,
tileSize: 512
});
// Add to map (but this won't work properly with EPSG:3857 map)
// This is more for demonstration - would need to change map CRS
map.addLayer(layerRef.current);
}
return () => {
if (layerRef.current && map) {
map.removeLayer(layerRef.current);
}
};
}, [map, url, layer, style, tilematrixSet, format, attribution, maxZoom]);
return null;
}

View File

@@ -13,14 +13,32 @@ export const mapLayers = {
url: "https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png",
maxZoom: 19
},
polishOrthophoto,
{
name: "Polish Land Records (WMS)",
attribution: '&copy; <a href="https://www.gugik.gov.pl/">GUGiK</a>',
// This is actually a WMS service, not WMTS as discovered from GetCapabilities
url: "https://integracja.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow?SERVICE=WMS&VERSION=1.3.0&REQUEST=GetMap&BBOX={bbox-epsg-3857}&CRS=EPSG:3857&WIDTH=256&HEIGHT=256&LAYERS=EGiB&STYLES=&FORMAT=image/png",
name: "🇵🇱 Polish Orthophoto (Standard)",
attribution: '&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>',
url: "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg",
maxZoom: 19,
tileSize: 256
checked: false
},
{
name: "🇵🇱 Polish Orthophoto (High Resolution)",
attribution: '&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>',
url: "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/HighResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg",
maxZoom: 19,
checked: false
},
{
name: "🌍 Google Satellite",
attribution: '&copy; Google',
url: "http://mt1.google.com/vt/lyrs=s&hl=pl&x={x}&y={y}&z={z}",
maxZoom: 20,
checked: false
}, {
name: "🌍 Google Hybrid",
attribution: '&copy; Google',
url: "http://mt1.google.com/vt/lyrs=y&hl=pl&x={x}&y={y}&z={z}",
maxZoom: 20,
checked: false
},
{
name: "Satellite (Esri)",
@@ -34,7 +52,131 @@ export const mapLayers = {
url: "https://{s}.basemaps.cartocdn.com/rastertiles/voyager/{z}/{x}/{y}{r}.png",
maxZoom: 19
}
].filter(Boolean) // Remove any null entries
].filter(Boolean), // Remove any null entries
overlays: [
{
name: "🌍 Google Roads",
type: "tile",
attribution: '&copy; Google',
url: "http://mt1.google.com/vt/lyrs=h&hl=pl&x={x}&y={y}&z={z}",
maxZoom: 20,
opacity: 1.0,
checked: false
},
{
name: "📋 Polish Cadastral Data",
type: "wms",
attribution: '&copy; <a href="https://www.gugik.gov.pl/">GUGiK</a>',
url: "https://integracja01.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow",
params: {
service: 'WMS',
request: 'GetMap',
layers: 'powiaty,powiaty_obreby,zsin,obreby,dzialki,geoportal,numery_dzialek,budynki',
styles: ',,,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857'
},
opacity: 0.8,
checked: false
},
{
name: "🏗️ Polish Spatial Planning",
type: "wms",
attribution: '&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>',
url: "https://mapy.geoportal.gov.pl/wss/ext/KrajowaIntegracjaMiejscowychPlanowZagospodarowaniaPrzestrzennego",
params: {
service: 'WMS',
request: 'GetMap',
layers: 'raster,wektor-str,wektor-lzb,wektor-pow,wektor-lin,wektor-pkt,granice',
styles: ',,,,,,',
format: 'image/png',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
tiled: true
},
opacity: 0.7,
checked: false
},
{
name: "🛣️ LP-Portal Roads",
type: "wms",
attribution: '&copy; <a href="https://lp-portal.pl/">LP-Portal</a>',
url: "https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms",
params: {
service: 'WMS',
request: 'GetMap',
layers: '00_6_mapainteraktywna_drogi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.3.0',
crs: 'EPSG:3857',
tiled: true
},
opacity: 0.9,
checked: false
},
{
name: "🏷️ LP-Portal Street Names",
type: "wms",
attribution: '&copy; <a href="https://lp-portal.pl/">LP-Portal</a>',
url: "https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms",
params: {
service: 'WMS',
request: 'GetMap',
layers: '00_5_mapainteraktywna_opisy_drog,00_3_mapainteraktywna_nazwy_ulic',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
tiled: true
},
opacity: 1.0,
checked: false
},
{
name: "📐 LP-Portal Parcels",
type: "wms",
attribution: '&copy; <a href="https://lp-portal.pl/">LP-Portal</a>',
url: "https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms",
params: {
service: 'WMS',
request: 'GetMap',
layers: 'dzialki',
styles: '',
format: 'image/png',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
tiled: true
},
opacity: 0.6,
checked: false
},
{
name: "📍 LP-Portal Survey Markers",
type: "wms",
attribution: '&copy; <a href="https://lp-portal.pl/">LP-Portal</a>',
url: "https://geoserver.lp-portal.pl/geoserver/pzdnowysacz/wms",
params: {
service: 'WMS',
request: 'GetMap',
layers: '00_2_view_geop_odcinek_pikietaz_glob_multi',
styles: '',
format: 'image/png8',
transparent: true,
version: '1.1.1',
crs: 'EPSG:3857',
tiled: true
},
opacity: 0.8,
checked: false
}
]
};
// WMTS services configuration with GetCapabilities URLs

View File

@@ -53,13 +53,15 @@ export function buildWMTSTileUrl(config) {
export const polishWMTSServices = {
orthophoto: {
name: "Polish Geoportal Orthophoto",
capabilitiesUrl: "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution",
capabilitiesUrl: "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/REST/StandardResolution",
layer: "ORTO",
style: "default",
tilematrixSet: "EPSG:2180",
format: "image/jpeg",
tilematrixSet: "EPSG:3857", // Changed from EPSG:2180 to EPSG:3857 for better Leaflet compatibility
format: "image/png", // Changed from jpeg to png
attribution: '&copy; <a href="https://www.geoportal.gov.pl/">Geoportal</a>',
maxZoom: 19
maxZoom: 19,
tileSize: 512,
zoomOffset: -1
}
};
@@ -75,14 +77,10 @@ export function generateLayerConfig(serviceName) {
return {
name: service.name,
attribution: service.attribution,
url: buildWMTSTileUrl({
baseUrl: service.capabilitiesUrl,
layer: service.layer,
style: service.style,
tilematrixSet: service.tilematrixSet,
format: service.format
}),
// Use working WMTS KVP URL format
url: "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?SERVICE=WMTS&REQUEST=GetTile&VERSION=1.0.0&LAYER=ORTO&STYLE=default&TILEMATRIXSET=EPSG:3857&TILEMATRIX={z}&TILEROW={y}&TILECOL={x}&FORMAT=image/jpeg",
maxZoom: service.maxZoom,
tileSize: 256
tileSize: 256, // Standard tile size for WMTS
checked: false
};
}