Spaces:
Running
Running
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}
/>
)
}
|