Files
panel/docs/MAP_SYSTEM_UPDATE_PLAN.md

789 lines
22 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Map System - Comprehensive Update & Fix Plan
Based on layer testing results from LAYER_NOTES.md
Date: January 16, 2026
---
## Executive Summary
**Current Status:**
-**6/7 base layers working** (1 broken: Polish Orthophoto High Resolution)
- ⚠️ **2/9 overlay layers working** (2 very slow, 5 not tested, 2 broken)
- 🎯 **Priority:** Fix broken layers, optimize slow WMS services, remove LP-Portal layers
**Key Issues Identified:**
1. Polish Orthophoto High Resolution completely broken
2. Polish Cadastral Data servers extremely slow (both servers)
3. Polish Spatial Planning layer not working
4. LP-Portal layers not tested/documented - likely region-specific
5. No caching or performance optimization for WMS layers
6. Missing zoom level restrictions causing tile request failures
---
## Phase 1: Critical Fixes (Week 1)
### 1.1 Fix Polish Orthophoto High Resolution
**Issue:** Doesn't load at all
**Root Cause:** Likely incorrect WMTS parameters or service endpoint change
**Action Plan:**
1. Test GetCapabilities response:
```bash
curl "https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/HighResolution?Service=WMTS&Request=GetCapabilities"
```
2. Compare with Standard Resolution working configuration
3. Check for:
- Different tile matrix sets
- Different available zoom levels
- Format differences (jpeg vs png)
- Authentication requirements
**Implementation:**
```javascript
// Test if service requires different parameters
{
name: "🇵🇱 Polish Orthophoto (High Resolution)",
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, // May need adjustment based on GetCapabilities
minZoom: 15, // High-res often only available at higher zoom
}
```
**Success Criteria:** Layer loads tiles without errors
---
### 1.2 Fix Polish Spatial Planning Layer
**Issue:** Doesn't work or extremely slow
**Service:** `https://mapy.geoportal.gov.pl/wss/ext/KrajowaIntegracjaMiejscowychPlanowZagospodarowaniaPrzestrzennego`
**Action Plan:**
1. Verify service is still active via GetCapabilities
2. Test with simplified layer list (may be requesting too many layers)
3. Check if service moved to new endpoint
4. Test with different WMS versions (1.1.1 vs 1.3.0)
**Implementation:**
```javascript
// Simplified layer request
{
name: "🏗️ Polish Spatial Planning",
type: "wms",
url: "https://mapy.geoportal.gov.pl/wss/ext/KrajowaIntegracjaMiejscowychPlanowZagospodarowaniaPrzestrzennego",
params: {
layers: "raster", // Start with just raster
format: "image/png",
transparent: true,
version: "1.3.0",
},
maxZoom: 18, // Limit to prevent overload
}
```
**Success Criteria:** Layer loads or is removed if permanently unavailable
---
### 1.3 Optimize Polish Cadastral Data Performance
**Issue:** Both servers very slow, currently only work up to zoom 18
**Impact:** Core functionality for land surveying projects
**Action Plan:**
1. Implement tile loading indicators
2. Add request debouncing
3. Consider caching strategy
4. Test alternate GUGiK services
5. (Future) Enable zoom 19-20 with proper optimization
**Implementation:**
```javascript
// Update both cadastral servers with performance optimizations
{
name: "📋 Polish Cadastral Data (Działki) - Server 2",
type: "wms",
url: "https://integracja.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow",
params: {
layers: "dzialki,numery_dzialek,budynki", // Simplified - remove slow layers
format: "image/png",
transparent: true,
version: "1.3.0",
},
maxZoom: 18, // Current working limit (TODO: extend to 20 with optimization)
minZoom: 13, // Don't load at far zoom levels
opacity: 0.8,
}
```
**Additional Optimizations:**
- Add WMS tiled parameter: `tiled: true`
- Reduce requested layers to essential only
- Implement progressive loading (load parcels first, then details)
**Success Criteria:** Acceptable load times (<3s) at zoom 15-18, prepare for zoom 20 support
---
## Phase 2: Layer Management (Week 2)
### 2.1 Remove/Document LP-Portal Layers
**Issue:** 4 LP-Portal layers never tested, likely region-specific (Nowy Sącz)
**Action Plan:**
1. Test if LP-Portal layers work outside Nowy Sącz region
2. If region-specific: Move to separate optional configuration
3. Document geographic limitations
4. Consider conditional loading based on map center coordinates
**Options:**
**Option A - Remove Entirely:**
```javascript
// Remove from mapLayers.overlays array:
// - LP-Portal Roads
// - LP-Portal Street Names
// - LP-Portal Parcels
// - LP-Portal Survey Markers
```
**Option B - Conditional Loading:**
```javascript
// Only show LP-Portal layers when in Nowy Sącz region
const isInNowySecz = (lat, lng) => {
return lat >= 49.5 && lat <= 49.7 && lng >= 20.5 && lng <= 20.8;
};
// Filter overlays based on location
const availableOverlays = mapLayers.overlays.filter(layer => {
if (layer.name.includes('LP-Portal')) {
return isInNowySecz(mapCenter[0], mapCenter[1]);
}
return true;
});
```
**Recommendation:** Option B - Keep but make conditional
**Success Criteria:** Only relevant layers shown to users
---
### 2.2 Reorganize Layer Categories
**Current:** Mixed organization, no clear hierarchy
**Proposed:** Clear categorization with user-friendly names
**New Structure:**
```javascript
export const mapLayers = {
base: [
// International Base Maps
{ name: "OpenStreetMap", ... },
{ name: "🌍 Google Satellite", ... },
{ name: "🌍 Google Hybrid", ... },
{ name: "🗺️ Esri Satellite", ... },
{ name: "🗺️ Topographic (CARTO)", ... },
// Polish Aerial Imagery
{ name: "🇵🇱 Orthophoto (Standard)", ... },
{ name: "🇵🇱 Orthophoto (High-Res)", ... }, // After fix
],
overlays: {
government: [
{ name: "📋 Cadastral Data (Official)", ... },
{ name: "🏗️ Spatial Planning", ... },
],
utility: [
{ name: "🛣️ Google Roads", ... },
],
regional: [ // Only shown in specific regions
{ name: "🏘️ LP-Portal Roads", region: "nowysacz", ... },
{ name: "🏘️ LP-Portal Street Names", region: "nowysacz", ... },
{ name: "🏘️ LP-Portal Parcels", region: "nowysacz", ... },
{ name: "🏘️ LP-Portal Survey Markers", region: "nowysacz", ... },
]
}
};
```
**Success Criteria:** Clearer user experience, better organization
---
## Phase 3: Performance Optimization (Week 3)
### 3.1 Implement Tile Caching
**Goal:** Reduce redundant WMS requests
**Implementation:**
```javascript
// Add to WMSLayer component
const WMSLayer = ({ url, params, opacity, attribution }) => {
const map = useMap();
useEffect(() => {
const wmsOptions = {
// ... existing options ...
// Add caching headers
crossOrigin: true,
updateWhenIdle: true,
updateWhenZooming: false,
keepBuffer: 2, // Keep tiles loaded from 2 screens away
};
const wmsLayer = L.tileLayer.wms(url, wmsOptions);
wmsLayer.addTo(map);
return () => map.removeLayer(wmsLayer);
}, [map, url, params, opacity, attribution]);
};
```
**Success Criteria:** 30% reduction in WMS requests on pan/zoom
---
### 3.2 Add Loading States
**Goal:** User feedback during slow WMS loads
**Implementation:**
```javascript
// New LoadingOverlay component
function MapLoadingOverlay({ isLoading }) {
if (!isLoading) return null;
return (
<div className="absolute top-16 right-4 bg-white dark:bg-gray-800 rounded-lg shadow-lg px-4 py-2 z-[1000]">
<div className="flex items-center gap-2">
<div className="animate-spin rounded-full h-4 w-4 border-2 border-blue-500 border-t-transparent"></div>
<span className="text-sm text-gray-700 dark:text-gray-300">Loading map layers...</span>
</div>
</div>
);
}
// Track loading state in LeafletMap
const [isLoading, setIsLoading] = useState(false);
useEffect(() => {
map.on('layeradd', () => setIsLoading(true));
map.on('load', () => setIsLoading(false));
}, [map]);
```
**Success Criteria:** Visual feedback for all layer loads
---
### 3.3 Implement Progressive Layer Loading
**Goal:** Load essential layers first, details later
**Strategy:**
1. **Zoom 1-12:** Base map only
2. **Zoom 13-15:** + Basic cadastral boundaries
3. **Zoom 16-18:** + Parcel numbers, buildings
4. **Zoom 19-20:** + Survey markers, detailed overlays
**Implementation:**
```javascript
// Auto-enable/disable overlays based on zoom
function ZoomBasedOverlayManager() {
const map = useMap();
const [currentZoom, setCurrentZoom] = useState(map.getZoom());
useEffect(() => {
map.on('zoomend', () => {
const zoom = map.getZoom();
setCurrentZoom(zoom);
// Auto-manage overlay visibility
if (zoom < 13) {
// Disable all overlays at far zoom
disableAllOverlays();
} else if (zoom >= 16) {
// Enable cadastral at close zoom
enableCadastralLayer();
}
});
}, [map]);
}
```
**Success Criteria:** Smooth performance at all zoom levels
---
## Phase 4: Enhanced Features (Week 4)
### 4.1 Dynamic Opacity Controls
**Goal:** User-adjustable layer transparency
**Implementation:**
```javascript
// LayerOpacityControl component
function LayerOpacityControl({ layerName, currentOpacity, onOpacityChange }) {
return (
<div className="flex items-center gap-2 px-2 py-1">
<label className="text-xs text-gray-600 dark:text-gray-400 w-24 truncate">
{layerName}
</label>
<input
type="range"
min="0"
max="100"
value={currentOpacity * 100}
onChange={(e) => onOpacityChange(e.target.value / 100)}
className="flex-1 h-1"
/>
<span className="text-xs text-gray-500 w-8 text-right">
{Math.round(currentOpacity * 100)}%
</span>
</div>
);
}
// Add to layer control
<LayersControl position="topright">
<Overlay name="Cadastral Data">
<WMSLayer {...layer} opacity={cadastralOpacity} />
</Overlay>
<LayerOpacityControl
layerName="Cadastral"
currentOpacity={cadastralOpacity}
onOpacityChange={setCadastralOpacity}
/>
</LayersControl>
```
**Success Criteria:** User can adjust opacity for all overlay layers
---
### 4.2 Layer Information Panels
**Goal:** Show layer metadata, legends, data source info
**Implementation:**
```javascript
// Layer metadata structure
const layerMetadata = {
"Polish Cadastral Data": {
title: "Polish Cadastral Data (Działki)",
description: "Official land parcel boundaries and property information from GUGiK",
dataSource: "Główny Urząd Geodezji i Kartografii",
updateFrequency: "Daily",
coverage: "Poland nationwide",
legend: "/images/legends/cadastral.png",
moreInfo: "https://www.gugik.gov.pl/",
usageNotes: "Best viewed at zoom levels 15-18. Performance may vary.",
}
};
// InfoButton component next to layer name
<LayersControl>
<Overlay name={
<div className="flex items-center gap-1">
📋 Cadastral Data
<button onClick={() => showLayerInfo('Polish Cadastral Data')} className="...">
</button>
</div>
}>
...
</Overlay>
</LayersControl>
```
**Success Criteria:** Users understand what each layer shows
---
### 4.3 Error Handling & Fallbacks
**Goal:** Graceful degradation when layers fail
**Implementation:**
```javascript
// WMSLayer with error handling
function WMSLayer({ url, params, opacity, attribution, fallbackLayer }) {
const map = useMap();
const [hasError, setHasError] = useState(false);
useEffect(() => {
const wmsLayer = L.tileLayer.wms(url, wmsOptions);
// Track tile errors
wmsLayer.on('tileerror', (error) => {
console.error(`WMS tile error for ${params.layers}:`, error);
setHasError(true);
// Show user notification
showNotification({
type: 'warning',
message: `Layer "${params.layers}" is experiencing issues`,
duration: 5000
});
});
wmsLayer.addTo(map);
// If too many errors, switch to fallback
if (hasError && fallbackLayer) {
setTimeout(() => {
map.removeLayer(wmsLayer);
fallbackLayer.addTo(map);
}, 3000);
}
return () => map.removeLayer(wmsLayer);
}, [map, url, params, hasError]);
}
```
**Success Criteria:** No silent failures, users informed of issues
---
## Phase 5: Code Quality (Week 5)
### 5.1 Consolidate Map Components
**Current Issue:** Multiple similar map components (ComprehensivePolishMap, ImprovedPolishOrthophotoMap, etc.)
**Action Plan:**
1. Audit all map components:
- LeafletMap.js (main)
- ProjectMap.js (wrapper)
- ComprehensivePolishMap.js
- ImprovedPolishOrthophotoMap.js
- PolishOrthophotoMap.js
- AdvancedPolishOrthophotoMap.js
- TransparencyDemoMap.js
- CustomWMTSMap.js
- EnhancedLeafletMap.js
2. Determine which are:
- Production (keep)
- Deprecated (remove)
- Experimental (move to /docs/examples)
**Recommendation:**
```
KEEP:
- LeafletMap.js (main production component)
- ProjectMap.js (SSR wrapper)
MOVE TO /docs/examples:
- TransparencyDemoMap.js (example of opacity controls)
- CustomWMTSMap.js (example of custom WMTS)
DEPRECATE/REMOVE:
- ComprehensivePolishMap.js (superseded by LeafletMap)
- ImprovedPolishOrthophotoMap.js (experimental)
- PolishOrthophotoMap.js (old version)
- AdvancedPolishOrthophotoMap.js (experimental)
- EnhancedLeafletMap.js (duplicate?)
```
**Success Criteria:** Single source of truth for map rendering
---
### 5.2 Improve WMTS Capabilities Parsing
**Current Issue:** wmtsCapabilities.js has placeholder code
**Options:**
**Option A - Complete XML Parsing:**
```javascript
export async function parseWMTSCapabilities(url) {
const response = await fetch(`${url}?Service=WMTS&Request=GetCapabilities`);
const xmlText = await response.text();
const parser = new DOMParser();
const xml = parser.parseFromString(xmlText, 'text/xml');
const layers = Array.from(xml.querySelectorAll('Layer')).map(layer => ({
id: layer.querySelector('Identifier')?.textContent,
title: layer.querySelector('Title')?.textContent,
formats: Array.from(layer.querySelectorAll('Format')).map(f => f.textContent),
tileMatrixSets: Array.from(layer.querySelectorAll('TileMatrixSet')).map(t => t.textContent),
}));
return { layers };
}
```
**Option B - Remove and Document:**
- Remove wmtsCapabilities.js
- Document WMTS configuration in MAP_LAYERS.md
- Use manual configuration (current working approach)
**Recommendation:** Option B - Keep it simple, current approach works
**Success Criteria:** No dead code, clear documentation
---
### 5.3 Add TypeScript/JSDoc Types
**Goal:** Better IDE support and type safety
**Implementation:**
```javascript
/**
* @typedef {Object} LayerConfig
* @property {string} name - Display name for the layer
* @property {'tile'|'wms'} type - Layer type
* @property {string} url - Service URL
* @property {string} attribution - Attribution HTML
* @property {number} [maxZoom=20] - Maximum zoom level
* @property {number} [minZoom=0] - Minimum zoom level
* @property {number} [opacity=1.0] - Layer opacity (0-1)
* @property {boolean} [checked=false] - Default enabled state
* @property {Object} [params] - WMS parameters (for WMS layers)
*/
/**
* @typedef {Object} MapLayersConfig
* @property {LayerConfig[]} base - Base layer options
* @property {LayerConfig[]} overlays - Overlay layer options
*/
/** @type {MapLayersConfig} */
export const mapLayers = {
base: [...],
overlays: [...]
};
```
**Success Criteria:** Better autocomplete and error detection
---
## Phase 6: Testing & Documentation (Week 6)
### 6.1 Create Layer Test Suite
**Goal:** Automated testing of layer availability
**Implementation:**
```javascript
// __tests__/map-layers.test.js
describe('Map Layers', () => {
describe('Base Layers', () => {
test('OSM tiles are accessible', async () => {
const response = await fetch('https://a.tile.openstreetmap.org/15/17560/11326.png');
expect(response.status).toBe(200);
});
test('Polish Orthophoto Standard is accessible', async () => {
const url = 'https://mapy.geoportal.gov.pl/wss/service/PZGIK/ORTO/WMTS/StandardResolution?Service=WMTS&Request=GetCapabilities';
const response = await fetch(url);
expect(response.status).toBe(200);
});
});
describe('WMS Overlays', () => {
test('Cadastral WMS GetCapabilities works', async () => {
const url = 'https://integracja.gugik.gov.pl/cgi-bin/KrajowaIntegracjaEwidencjiGruntow?Service=WMS&Request=GetCapabilities';
const response = await fetch(url);
expect(response.status).toBe(200);
expect(response.headers.get('content-type')).toContain('xml');
});
});
});
```
**Success Criteria:** All layers validated before deployment
---
### 6.2 Update Documentation
**Files to Update:**
1. MAP_LAYERS.md - Add troubleshooting section
2. LAYER_NOTES.md - Keep updated with testing
3. README.md - Add maps usage section
**New Documentation:**
```markdown
## Troubleshooting Map Layers
### Slow Loading Cadastral Data
- **Cause:** GUGiK WMS servers are resource-limited
- **Solution:** Only enable at zoom 15+, limit to essential layers
- **Alternative:** Pre-cache frequently used areas
### Polish Orthophoto Not Loading
- **Check:** Zoom level (works up to 19, not 20)
- **Check:** Network connectivity to geoportal.gov.pl
- **Alternative:** Use Google Satellite or Esri
### Layer Control Not Showing
- **Cause:** Map container too small
- **Solution:** Minimum map height of 400px recommended
```
**Success Criteria:** Users can self-service common issues
---
## Implementation Priority Matrix
| Priority | Phase | Task | Impact | Effort | Status |
|----------|-------|------|--------|--------|--------|
| 🔴 P0 | 1 | Fix Polish Orthophoto High-Res | High | Low | Not Started |
| 🔴 P0 | 1 | Add zoom restrictions to Cadastral | High | Low | Not Started |
| 🟡 P1 | 1 | Fix/Remove Spatial Planning | Medium | Medium | Not Started |
| 🟡 P1 | 2 | Document LP-Portal region limits | Medium | Low | Not Started |
| 🟡 P1 | 3 | Add loading indicators | Medium | Low | Not Started |
| 🟢 P2 | 2 | Reorganize layer categories | Low | Medium | Not Started |
| 🟢 P2 | 4 | Add opacity controls | Low | Medium | Not Started |
| 🟢 P2 | 4 | Add layer info panels | Low | High | Not Started |
| 🟢 P3 | 5 | Consolidate map components | Low | High | Not Started |
| 🟢 P3 | 6 | Add automated tests | Low | Medium | Not Started |
---
## Quick Wins (Do First)
These can be implemented in 1-2 hours with immediate impact:
1. **Add minZoom to performance-heavy layers**
- Prevent loading at far zoom levels (minZoom: 13 for Cadastral)
- Reduce unnecessary requests at distant zoom
2. **Optimize Cadastral layer requests**
- Reduce number of requested WMS layers
- Add tiled parameter for better performance
3. **Remove broken Spatial Planning layer**
- If GetCapabilities fails, just remove it
- Better than showing broken functionality
4. **Update layer names for clarity**
- "Polish Orthophoto Standard" → "🇵🇱 Aerial Imagery (Standard)"
- Better user understanding
5. **Add loading spinner to ProjectMap**
- Copy LoadingOverlay component
- Better UX during slow loads
6. **Verify current zoom limits**
- Document actual working zoom ranges per layer
- Note: Goal is zoom 20 for all layers (future optimization)
---
## Success Metrics
### Performance
- [ ] All base layers load in <2 seconds
- [ ] Cadastral overlays load in <5 seconds at zoom 15-18
- [ ] No console errors for working layers
- [ ] 90%+ tile success rate
### User Experience
- [ ] Layer control accessible on all screen sizes
- [ ] Clear feedback during loading
- [ ] No broken/blank layers in production
- [ ] Layer purposes clear from names/descriptions
### Code Quality
- [ ] Single production map component
- [ ] All map files under 500 lines
- [ ] JSDoc types for all exports
- [ ] 80%+ test coverage for layer configs
---
## Rollout Plan
### Week 1: Emergency Fixes
- Fix critical broken layers
- Add zoom restrictions
- Remove non-working layers
### Week 2: Optimization
- Implement caching
- Add loading states
- Progressive loading
### Week 3: Features
- Opacity controls
- Layer info panels
- Error handling
### Week 4: Cleanup
- Consolidate components
- Remove experimental code
- Update documentation
### Week 5: Testing
- Automated tests
- User acceptance testing
- Performance benchmarking
### Week 6: Release
- Deploy to production
- Monitor performance
- Gather user feedback
---
## Rollback Strategy
If issues occur:
1. **Keep old mapLayers.js** as `mapLayers.backup.js`
2. **Feature flags** for new functionality
3. **Incremental rollout** - enable for admin users first
4. **Quick disable** - config flag to revert to old layers
---
## Future Considerations
### Potential New Features
- [ ] **Universal zoom 20 support for all layers**
- Optimize WMS services to handle zoom 19-20
- Implement tile prefetching and caching
- Add progressive detail loading at high zoom
- [ ] Save user layer preferences
- [ ] Share map view URLs (with layers/zoom)
- [ ] Export map as image/PDF
- [ ] Offline tile caching
- [ ] Custom layer upload (GeoJSON, KML)
### Alternative Services to Explore
- [ ] Planet imagery (if budget allows)
- [ ] Bing Maps aerial imagery
- [ ] Additional Polish regional services
- [ ] CORS proxies for restricted services
### Advanced Optimizations
- [ ] Service worker for tile caching
- [ ] WebGL rendering for better performance
- [ ] Vector tiles instead of raster
- [ ] CDN for frequently accessed tiles
---
## Notes
- LP-Portal layers appear to be **Nowy Sącz specific** - need regional filtering
- Polish government servers are **consistently slow** - can't fix, only mitigate
- Google services are **unofficial** - may break without notice
- WMTS is more performant than WMS - prefer when available
- **Zoom 20 support:** Long-term goal for all layers; currently some layers work only to zoom 18-19
- Requires server-side optimization or caching strategy
- May need to implement client-side tile scaling/interpolation
- Keep LAYER_NOTES.md updated as testing continues
---
## Approval & Sign-off
- [ ] Technical review completed
- [ ] Performance benchmarks met
- [ ] Documentation updated
- [ ] Stakeholder approval
- [ ] Ready for production deployment
**Last Updated:** January 16, 2026
**Next Review:** After Phase 1 completion