npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@lucabeapps/react-network-map

v0.2.0

Published

Componente React para exibir redes elétricas (postes, rede BT/MT, transformadores...) sobre OpenStreetMap com MapLibre GL. Busca dados por bounding box e é totalmente customizável.

Readme

@lucabeapps/react-network-map

Componente React para exibir redes elétricas (postes, rede BT/MT, transformadores, chaves, iluminação…) sobre OpenStreetMap, usando MapLibre GL. Busca as features por bounding box conforme o usuário navega e é totalmente customizável.

  • 🗺️ Base OpenStreetMap (ou qualquer tile/estilo MapLibre)
  • ⚡ WebGL — aguenta milhares de pontos/linhas, com clustering opcional
  • 🔌 Fonte de dados plugável (sua API por bbox, com adapter pronto)
  • 🎨 Estilos por camada, controle de liga/desliga, popups customizáveis
  • 📦 TypeScript, ESM + CJS, tree-shakeable

Instalação

npm install @lucabeapps/react-network-map maplibre-gl react react-dom

maplibre-gl, react e react-dom são peer dependencies.

Importe o CSS do MapLibre uma vez no seu app:

import "maplibre-gl/dist/maplibre-gl.css";

Uso rápido

import { NetworkMap, createBboxProvider } from "@polestrack/react-network-map";
import "maplibre-gl/dist/maplibre-gl.css";

const provider = createBboxProvider({
  url: "https://poles.lucabeapps.com.br/features",   // sua API (polestrack-api)
  schema: "enel_sp_2024",
});

export default function App() {
  return (
    <div style={{ height: "100vh" }}>
      <NetworkMap
        provider={provider}
        initialViewState={{ longitude: -46.633, latitude: -23.55, zoom: 15 }}
        minZoom={13}
        onFeatureClick={(f) => console.log(f.properties)}
      />
    </div>
  );
}

O contêiner precisa ter altura definida (ex.: height: 100vh ou flex: 1).

Sem escolher a distribuidora (auto-descoberta)

Se você não quer que o usuário selecione a distribuidora, use autoLocate: o mapa descobre sozinho qual concessão cobre a área conforme navega (e re-descobre ao entrar noutra). Basta não passar schema:

const provider = createBboxProvider({
  url: "https://poles.lucabeapps.com.br/features",
  autoLocate: true,                       // acha a distribuidora sozinho
  onLocate: (schema) => console.log("distribuidora:", schema), // opcional
});

Requer o endpoint GET {base}/locate?west&south&east&north → { "schemas": [...] } (o polestrack-api já expõe). Por padrão a URL é derivada trocando /features por /locate; se sua rota for outra, passe locateUrl.

Nota: a biblioteca é só o mapa (visualização). Cadastro de postes, análise por câmera (AR) e importação de KML são recursos do app que consome a lib, não fazem parte do pacote.


De onde vêm os dados

A lib chama o provider sempre que o mapa é movido (acima de minZoom), passando a bounding box atual. O padrão é createBboxProvider, que faz um GET no seu endpoint:

GET {url}?west=..&south=..&east=..&north=..&zoom=..&schema=..&layers=postes,rede_bt,..

e espera um GeoJSON FeatureCollection cujas features tenham properties._layer (mesmo formato do conectagd/do banco gerado pelo scraper mapear-brasil). Um servidor de referência pronto acompanha o projeto mapear-brasil (api-server.mjs).

Se o formato do seu backend for diferente, use transform:

createBboxProvider({ url, transform: (raw) => ({ type: "FeatureCollection", features: raw.dados }) });

CORS / cookies (importante)

Por padrão o fetch usa credentials: "same-origin", que funciona com APIs públicas que respondem Access-Control-Allow-Origin: * (o caso da polestrack-api).

⚠️ Não combine credentials: "include" com Allow-Origin: * — o navegador bloqueia a requisição silenciosamente (o mapa fica vazio). Só use createBboxProvider({ ..., credentials: "include" }) se sua API precisa de cookie cross-site — e aí ela precisa refletir a origem exata no CORS + Access-Control-Allow-Credentials: true (não pode usar *).

Garanta também que o CORS_ORIGIN da API inclua o domínio do seu front.

Ou passe sua própria função direto, sem adapter:

<NetworkMap fetchFeatures={async ({ bbox, zoom, layers, signal }) => {
  const r = await fetch(`/api?…`, { signal });
  return r.json();   // FeatureCollection com properties._layer
}} />

Customização

Estilos por camada

Os estilos do usuário estendem (não substituem) os padrões de cada camada:

<NetworkMap
  provider={provider}
  layers={["postes", "rede_bt", "rede_mt", "transformadores"]}  // só estas
  layerStyles={{
    postes: { color: "#00e5ff", radius: 4 },
    rede_mt: { color: "#ff3300", width: 3 },
    transformadores: { color: "#ffcc00", radius: 6, strokeColor: "#000", strokeWidth: 1 },
  }}
/>

Campos de LayerStyle: label, geometry (auto|point|line|polygon), color, radius, width, opacity, strokeColor, strokeWidth, visible, minzoom, maxzoom, e overrides brutos do MapLibre: circlePaint, linePaint, fillPaint.

Seletor de camadas por fora (sua UI)

O painel de camadas interno é um controle do mapa (bom em tela cheia, mas pode estourar dentro de um card pequeno). Para montar o seu seletor por fora, esconda o interno com showLayerToggle={false} e use onLayers — ele entrega a lista de camadas (rótulo, cor, forma do ícone, visível, contagem ao vivo) e um setVisible para ligar/desligar:

const [layers, setLayers] = useState<LayerInfo[]>([]);
const apiRef = useRef<LayersApi>();

<NetworkMap
  provider={provider}
  showLayerToggle={false}
  onLayers={(api) => { apiRef.current = api; setLayers(api.layers); }}
/>

{/* seu painel, onde você quiser */}
{layers.map((l) => (
  <label key={l.key}>
    <input type="checkbox" checked={l.visible}
      onChange={(e) => apiRef.current!.setVisible(l.key, e.target.checked)} />
    {l.label} {l.count != null && `(${l.count})`}
  </label>
))}

Os pontos são desenhados no mapa com o ícone da forma de cada camada (triângulo p/ transformador, losango, hexágono, "chave"…), iguais aos da legenda.

Clustering (agrupar pontos densos)

<NetworkMap provider={provider} cluster clusterRadius={60} />

Afeta só camadas de ponto; linhas continuam normais. Clicar num cluster dá zoom.

Popup customizado

<NetworkMap
  provider={provider}
  renderPopup={(f) => `<b>${f.properties._layer}</b><br/>ID: ${f.properties.cod_id}`}
/>

Retorne string, um HTMLElement, ou null para não abrir popup. Para painéis em React, use onFeatureClick e controle o estado no seu app.

Base map / tiles

<NetworkMap osmTileUrl="https://{a-c}.tile.openstreetmap.org/{z}/{x}/{y}.png" />
// ou um estilo MapLibre completo (vetorial, satélite, etc.):
<NetworkMap mapStyle="https://demotiles.maplibre.org/style.json" />

Acesso ao mapa (escape hatch)

<NetworkMap onLoad={(map) => { /* objeto maplibregl.Map — faça o que quiser */ }} />

Props

| Prop | Tipo | Padrão | Descrição | |------|------|--------|-----------| | provider | DataProvider | — | Fonte de dados (use createBboxProvider) | | fetchFeatures | (ctx) => Promise<FeatureCollection> | — | Alternativa ao provider | | layers | string[] | todas | Quais camadas renderizar | | layerStyles | Record<string, LayerStyle> | padrões | Estende os estilos por camada | | initialViewState | {longitude,latitude,zoom} | SP | View inicial | | minZoom | number | 13 | Abaixo disso não busca dados | | cluster | boolean | false | Agrupa pontos | | clusterRadius | number | 50 | Raio do cluster (px) | | osmTileUrl | string | OSM | URL dos tiles raster | | mapStyle | StyleSpecification\|string | — | Override total do estilo base | | debounceMs | number | 300 | Debounce das buscas ao mover | | showControls | boolean | true | Zoom/bússola/escala | | showLayerToggle | boolean | true | Painel liga/desliga camadas | | renderPopup | (f) => string\|HTMLElement\|null | tabela auto | Conteúdo do popup | | onFeatureClick | (f, ev) => void | — | Clique em feature | | onViewStateChange | (v) => void | — | Ao mover o mapa | | onError | (err) => void | — | Erro de busca | | onLoad | (map) => void | — | Acesso ao maplibregl.Map | | className / style | | | No contêiner |

Exports

NetworkMap, createBboxProvider, conectagdProvider, DEFAULT_LAYER_STYLES, DEFAULT_LAYER_ORDER, LayerToggleControl, e os tipos (NetworkMapProps, LayerStyle, DataProvider, NetworkFeature, …).


Desenvolvimento

npm install
npm run build       # gera dist/ (ESM + CJS + tipos)
npm run typecheck

cd example && npm install && npm run dev   # demo com dados mockados

Publicar no npm

npm login
npm publish --access public   # escopo @polestrack precisa de --access public

Licença

MIT