Files
panel/docs/MAP_SYSTEM_UPDATE_PLAN.md

22 KiB
Raw Permalink Blame History

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:
    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:

// 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:

// 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:

// 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:

// Remove from mapLayers.overlays array:
// - LP-Portal Roads
// - LP-Portal Street Names  
// - LP-Portal Parcels
// - LP-Portal Survey Markers

Option B - Conditional Loading:

// 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:

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:

// 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:

// 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:

// 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:

// 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:

// 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:

// 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:

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:

/**
 * @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:

// __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:

## 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