File size: 3,580 Bytes
e8db196
 
 
 
 
 
 
 
 
 
 
d6c9678
 
e8db196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
c88d3e9
 
 
 
 
 
 
 
 
 
 
 
e8db196
 
 
c88d3e9
 
 
 
e8db196
 
c88d3e9
e8db196
 
 
 
c88d3e9
 
 
 
e8db196
c88d3e9
e8db196
 
 
 
 
 
c88d3e9
 
 
 
 
 
e8db196
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
d6c9678
 
 
 
 
 
e8db196
 
d6c9678
 
 
 
e8db196
d6c9678
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
import React, { useCallback, useEffect, useMemo, useRef } from 'react'

function hashHtml(value) {
  let hash = 0
  const step = Math.max(1, Math.floor(value.length / 64))
  for (let i = 0; i < value.length; i += step) {
    hash = ((hash << 5) - hash) + value.charCodeAt(i)
    hash |= 0
  }
  return `${value.length}-${Math.abs(hash)}`
}

export default function MapFrame({ html }) {
  const iframeRef = useRef(null)
  const timersRef = useRef([])
  const frameKey = useMemo(() => hashHtml(html || ''), [html])

  const clearTimers = useCallback(() => {
    timersRef.current.forEach((id) => window.clearTimeout(id))
    timersRef.current = []
  }, [])

  const recenterFromLayers = useCallback(() => {
    const iframe = iframeRef.current
    if (!iframe) return

    try {
      const win = iframe.contentWindow
      const doc = iframe.contentDocument || win?.document
      if (!win || !doc || !win.L) return

      const maps = Object.values(win).filter(
        (item) => item
          && typeof item.fitBounds === 'function'
          && typeof item.eachLayer === 'function'
          && typeof item.invalidateSize === 'function',
      )

      const map = maps[0]
      if (!map) return

      map.invalidateSize(true)
      const dataBounds = win.L.latLngBounds([])

      const isBairroLayer = (layer) => {
        try {
          const nome = String(layer?.options?.name || layer?.layerName || '').toLowerCase()
          if (nome.includes('bairro')) return true
          const featureName = String(layer?.feature?.properties?.NOME || layer?.feature?.properties?.BAIRRO || '').toLowerCase()
          return featureName.length > 0
        } catch {
          return false
        }
      }

      map.eachLayer((layer) => {
        try {
          if (layer instanceof win.L.TileLayer) return
          if (isBairroLayer(layer)) return

          if (layer instanceof win.L.CircleMarker || layer instanceof win.L.Marker) {
            const latlng = layer.getLatLng()
            if (latlng && Number.isFinite(latlng.lat) && Number.isFinite(latlng.lng)) {
              dataBounds.extend(latlng)
            }
            return
          }

          if (layer instanceof win.L.Rectangle) {
            const rectBounds = layer.getBounds()
            if (rectBounds && typeof rectBounds.isValid === 'function' && rectBounds.isValid()) {
              dataBounds.extend(rectBounds)
            }
            return
          }
        } catch {
          // no-op
        }
      })

      if (dataBounds.isValid()) {
        const size = map.getSize ? map.getSize() : null
        const basePadding = size
          ? Math.max(34, Math.min(84, Math.round(Math.min(size.x, size.y) * 0.085)))
          : 48
        map.fitBounds(dataBounds, { padding: [basePadding, basePadding], maxZoom: 18, animate: false })
      }
    } catch {
      // no-op
    }
  }, [])

  const scheduleRecenter = useCallback(() => {
    clearTimers()
    ;[40, 180, 520, 1100].forEach((delay) => {
      const timerId = window.setTimeout(() => {
        recenterFromLayers()
      }, delay)
      timersRef.current.push(timerId)
    })
  }, [clearTimers, recenterFromLayers])

  useEffect(() => {
    return () => {
      clearTimers()
    }
  }, [clearTimers])

  if (!html) {
    return <div className="empty-box">Mapa indisponivel.</div>
  }

  return (
    <iframe
      key={frameKey}
      ref={iframeRef}
      title="mapa"
      className="map-frame"
      srcDoc={html}
      sandbox="allow-scripts allow-same-origin allow-popups"
      onLoad={scheduleRecenter}
    />
  )
}