Modelo de Visualização

Documentação do modelo de visualização.

Índice

Introdução

O viewTemplate é um componente central do sistema de visualização do mapa, usado para definir como as informações de polígonos ou outros elementos são renderizadas no frontend. Ele permite a criação de layouts personalizados para exibir dados de maneira estruturada e interativa, combinando wrappers (estruturas de container) e templates (componentes de conteúdo). Esta documentação detalha os tipos de wrapper e template disponíveis, explicando suas funcionalidades e casos de uso com exemplos práticos.

Usando EJS para Renderização

O sistema utiliza a linguagem de template EJS (Embedded JavaScript) para renderizar dinamicamente informações no viewTemplate. O EJS permite embutir lógica JavaScript diretamente nos templates, acessando propriedades de dados (como properties ou data) e gerando conteúdo condicional ou formatado. Por exemplo, expressões como <%- properties?.qt_area_terreno ?? '-' %> extraem valores de propriedades e fornecem um valor padrão (-) se os dados estiverem ausentes. O uso de EJS garante flexibilidade para criar visualizações ricas e adaptáveis ao usuário.

Wrappers

Wrappers são containers que organizam templates em estruturas visuais, como linhas, cartões ou listas. Eles definem o layout geral e podem conter outros wrappers ou templates como filhos.

Lista de Wrappers Disponíveis

Linha

O wrapper-row organiza templates em uma linha, aproveitando o sistema de grid do Bootstrap. Cada item no array templates corresponde a uma coluna no grid, permitindo layouts responsivos. Usuários podem especificar a largura de cada coluna usando a propriedade columnClass, que aceita qualquer classe de coluna do Bootstrap, como col-md-6 para meia largura em telas médias ou maiores.

  • Propósito: Agrupar templates horizontalmente, como pares de rótulo-valor em colunas.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do wrapper, deve ser "wrapper-row"."wrapper-row"
templatesArray de templates filhos ou wrappers a serem renderizados na linha.[{ "type": "label-value", ... }]
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
columnClassClasse CSS do Bootstrap para definir a largura da coluna para cada template."col-md-6"
  • Exemplo:

    {
      "type": "wrapper-row",
      "templates": [
        {
          "type": "label-value",
          "label": "Área do Terreno",
          "value": "<%- properties?.qt_area_terreno ?? '-' %>",
          "properties": {
            "columnClass": "col-md-6"
          }
        },
        {
          "type": "label-value",
          "label": "Área Construída",
          "value": "<%- properties?.qt_area_construida ?? '-' %>",
          "properties": {
            "columnClass": "col-md-6"
          }
        }
      ]
    }

    Este exemplo cria uma linha com dois pares de rótulo-valor, cada um ocupando metade da largura disponível em telas médias ou maiores, usando o grid do Bootstrap.

Cartão

O wrapper-card exibe conteúdo em um cartão visual, tipicamente com um título (label) e um corpo contendo outros templates ou wrappers. É usado para agrupar informações relacionadas de maneira visualmente distinta.

  • Propósito: Apresentar informações agrupadas, como detalhes de polígonos ou uma lista de interseções.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do wrapper, deve ser "wrapper-card"."wrapper-card"
labelTítulo do cartão exibido no topo."Informações"
templatesArray de templates filhos ou wrappers a serem renderizados no corpo.[{ "type": "wrapper-row", ... }]
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
helperTexto de ajuda{ "helper": "Informações sobre o conteúdo no cartão" }
  • Exemplo:

    {
      "type": "wrapper-card",
      "label": "Informações",
      "templates": [
        {
          "type": "wrapper-row",
          "templates": [
            {
              "type": "label-value",
              "label": "Área do Terreno",
              "value": "<%- properties?.qt_area_terreno ?? '-' %>",
              "properties": {
                "columnClass": "col-md-6"
              }
            },
            {
              "type": "label-value",
              "label": "Área Construída",
              "value": "<%- properties?.qt_area_construida ?? '-' %>",
              "properties": {
                "columnClass": "col-md-6"
              }
            }
          ]
        }
      ]
    }

    Este cartão exibe um título "Informações" e uma linha com dois pares de rótulo-valor.

Wrapper de Requisição

O wrapper-request realiza requisições HTTP para buscar dados a serem exibidos na interface, usando a configuração do Axios para definir os parâmetros da requisição. O resultado da requisição é armazenado na variável response no objeto principal, permitindo que templates filhos acessem esses dados para renderização.

  • Propósito: Buscar dinamicamente dados de uma API e exibi-los em templates filhos ou wrappers, como cartões ou listas.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do wrapper, deve ser "wrapper-request"."wrapper-request"
templatesArray de templates filhos ou wrappers a serem renderizados com os dados da requisição.[{ "type": "wrapper-card", ... }]
  • Propriedades do Objeto properties:

O objeto properties segue a tipagem da configuração de requisição do Axios (Axios), com a particularidade de que funções como transformRequest, transformResponse, paramsSerializer, e outras devem ser fornecidas como string functions (funções em formato de string), pois os dados são armazenados em JSON no banco de dados. As propriedades mais comuns incluem:

PropriedadeDescriçãoExemplo
urlURL do servidor para a requisição (obrigatório)."https://api.mapa.urbis.sampa.br/geospatial-intersections"
methodMétodo HTTP da requisição (padrão: "get")."post"
dataDados enviados no corpo da requisição, pode ser uma string function. O objeto passado para esta função é o objeto de configuração completo do formulário, contendo o polígono selecionado dentro da propriedade data."(data) => data"
transformResponseString function que transforma os dados da resposta antes de serem usados."(response) => { response = JSON.parse(response); return response; }"
  • Notas:

    • O resultado da requisição é armazenado na variável response no objeto principal, acessível via EJS em templates filhos, como <%- response?.properties?.key %>.
    • Funções devem ser serializáveis em JSON, então use string functions para configurações como transformResponse ou data.
    • A função data recebe o objeto de configuração completo do formulário, que inclui o polígono selecionado na propriedade data, permitindo acesso a informações como data.geometry para uso na requisição.
  • Exemplo:

    {
      "type": "wrapper-request",
      "properties": {
        "url": "https://api.mapa.urbis.sampa.br/geospatial-intersections",
        "method": "post",
        "data": "({data}) => data",
        "transformResponse": "(response) => { response = JSON.parse(response); const { features } = response; const fieldToLayerMap = { geom_zoneamento_2016: ['slui:zoneamento'], geom_subprefeitura: ['slui:subprefeitura'], geom_distrito: ['slui:distrito_municipal'], geom_tombado: ['slui:tombamentos-areas', 'slui:tombamentos-envoltorias-de-imoveis', 'slui:tombamentos-imoveis'], geom_uc: ['slui:parques_unidades_de_conservacao_e_apa'], geom_apa: ['slui:parques_unidades_de_conservacao_e_apa'], geom_area_contaminada: ['slui:areas_contaminadas'], geom_melhoramento_viario: ['slui:minianel_viario'], geom_area_manancial: ['slui:manancial_billings'], geom_area_manancial_guarapiranga: ['slui:manancial_guarapiranga'], geom_area_manancial_juquery: ['slui:manancial_juquery'], geom_area_envoltoria_iphan: ['slui:tombamentos_envoltorias_de_imoveis_IPHAN'], geom_area_envoltoria_conpresp: ['slui:tombamentos_envoltorias_de_imoveis_CONPRESP'], geom_area_envoltoria_condephaat: ['slui:tombamentos_envoltorias_de_imoveis_CONDEPHAAT'] }; const camadasTombamento = ['geom_tombado', 'geom_area_envoltoria_condephaat', 'geom_area_envoltoria_conpresp', 'geom_area_envoltoria_iphan']; const camadasPreservada = ['geom_apa', 'geom_area_manancial_juquery', 'geom_area_manancial_guarapiranga', 'geom_area_manancial_billings']; response.properties.cit_data = []; response.properties.restricoes = []; features.forEach((feat) => { const { properties } = feat; const { layer } = properties; if (properties?.cit_data?.situacao_do_imovel) response.properties.cit_data.push(properties.cit_data); Object.keys(fieldToLayerMap).forEach((key) => { if (fieldToLayerMap[key].includes(layer)) { response.properties.restricoes.push(key); } }); camadasTombamento.forEach((camada) => { if (response.properties.restricoes.includes(camada)) { response.properties.tombamento_restrito = true; } }); camadasPreservada.forEach((camada) => { if (response.properties.restricoes.includes(camada)) { response.properties.area_preservada = true; } }); }); return response; }"
      },
      "templates": [
        {
          "type": "wrapper-card",
          "label": "Restrições",
          "templates": [
            {
              "type": "label-value",
              "label": "ITBI",
              "value": "<%= 'Indisponível' %>"
            },
            {
              "type": "label-value",
              "label": "Tombamento",
              "value": "<%- response?.properties?.tombamento_restrito ? 'Tombado' : 'Sem restrição' %>"
            },
            {
              "type": "label-value",
              "label": "Área de Preservação Ambiental",
              "value": "<%- response?.properties?.area_preservada ? 'Preservada' : 'Sem restrição' %>"
            },
            {
              "type": "label-value",
              "label": "Árvores no Imóvel",
              "value": "<%= 'Indisponível' %>"
            }
          ]
        }
      ]
    }

    Este exemplo realiza uma requisição POST para a API de interseções geoespaciais, usando o objeto de configuração do formulário (contendo o polígono selecionado em data) para enviar os dados, transforma a resposta para mapear restrições e exibe os resultados em um cartão com quatro pares de rótulo-valor.

Itens de Lista

O wrapper-list-items renderiza uma lista de itens, frequentemente usado para exibir dados filtrados ou relacionados, como lotes ou interseções. Ele suporta interações, como cliques em itens, e pode exibir informações em formato de uma ou duas linhas (twoLine).

  • Propósito: Exibir listas dinâmicas, como lotes em um perímetro ou interseções geográficas, com suporte a ações de clique.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do wrapper, deve ser "wrapper-list-items"."wrapper-list-items"
templatesArray de templates, tipicamente Item Primário e Item Secundário.[{ "type": "primary-item", ... }]
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
dataFunção JavaScript executada quando o componente inicializa, retornando dados da lista obtidos da API de interseções geoespaciais."(data) => data.response.features.filter(({ id }) => id.includes('lote_cidadao'))"
twoLineBooleano que habilita o modo de duas linhas para itens primários e secundários.true
onItemClickConfiguração para a ação disparada quando um item é clicado.{ "action": "openFeature", "params": { "template": "root" } }
  • Exemplo:

    {
      "type": "wrapper-list-items",
      "properties": {
        "twoLine": true,
        "data": "(data) => data.response.features.filter(({ id }) => id.includes('lote_cidadao'))",
        "onItemClick": {
          "action": "openFeature",
          "params": { "template": "root" }
        }
      },
      "templates": [
        {
          "type": "primary-item",
          "value": "Identificador #<%- properties.id.replace('lote_cidadao.', '') %>"
        },
        {
          "type": "secondary-item",
          "value": "SQL: <%- properties.cd_setor_fiscal %>-<%- properties.cd_quadra_fiscal %>-<%- properties.cd_lote %> <%- properties.cd_condominio %> <%- properties.nm_logradouro_completo ?? '-' %>"
        }
      ]
    }

    Este exemplo renderiza uma lista de lotes com duas linhas por item, onde clicar em um item abre detalhes usando o viewTemplate da camada.

Templates

Templates são componentes que definem conteúdo específico a ser renderizado, como texto, mapas ou ações. Eles são frequentemente usados dentro de wrappers para exibir informações detalhadas.

Lista de Templates Disponíveis

Rótulo e Valor

O template label-value exibe um par de rótulo-valor, ideal para mostrar propriedades específicas do polígono, como área ou tipo.

  • Propósito: Apresentar informações concisas em um formato de rótulo e valor dinâmico.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do template, deve ser "label-value"."label-value"
labelTexto do rótulo exibido ao lado do valor."Área do Terreno"
valueExpressão EJS que define o valor a ser exibido."<%- properties?.qt_area_terreno ?? '-' %>"
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
columnClassClasse CSS do Bootstrap para grid layout, usado dentro de wrapper-row."col-md-6"
helperTexto de ajuda{ "helper": "Informações sobre o valor" }
  • Exemplo:

    {
      "type": "label-value",
      "label": "Área do Terreno",
      "value": "<%- properties?.qt_area_terreno ?? '-' %>",
      "properties": {
        "columnClass": "col-md-6"
      }
    }

    Este template exibe o rótulo "Área do Terreno" e o valor correspondente da propriedade qt_area_terreno.

Ação de Editar Polígono

O template edit-polygon é a ação para editar e iniciar um novo protocolo baseado no polígono.

  • Propósito: Editar o polígono e criar um protocolo com suas informações.
  • Propriedades Raiz:
PropriedadeDescriçãoPadrãoExemplo
typeIdentificador do template, deve ser "edit-polygon".-"edit-polygon"
labelO texto que será exibido no botão na tela."Ajustar Perímetro""edit-polygon"
polygonTemplateArray de wrappers ou templates a serem renderizados. Se não informado, o sistema buscará a configuração editFeatureTemplate que está na tabela map_config.-[{ "type": "wrapper-card", ... }]
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
(Nenhuma)Atualmente, não há propriedades específicas no objeto properties.-

Este template não será renderizado quando a visualização for para impressão

  • Exemplo:

    {
      "type": "edit-polygon",
      "polygonTemplate": [
        {
          "type": "wrapper-card",
          "label": "Área Selecionada",
          "templates": [
            {
              "type": "polygon-map",
              "properties": {
                "initialViewState": "(data) => { const centroid = utils.calculateCenterId(data.geometry.coordinates[0]); return { longitude: centroid[0], latitude: centroid[1], zoom: 16.5, pitch: 0, bearing: 0 }; }",
                "polygonProps": "(data) => ({ id: 'polygon-layer', data: [{ coordinates: data.geometry.coordinates }], pickable: false, stroked: true, filled: true, lineWidthMinPixels: 2, getPolygon: (d) => d.coordinates, getFillColor: [255, 165, 0, 100], getLineColor: [255, 140, 0] })"
              }
            }
          ]
        },
        {
          "type": "wrapper-card",
          "label": "Lotes no Perímetro",
          "templates": [
            {
              "type": "wrapper-list-items",
              "properties": {
                "twoLine": true,
                "data": "(data) => data.response.features.filter(({ id }) => id.includes('lote_cidadao'))",
                "onItemClick": {
                  "action": "openFeature",
                  "params": { "template": "root" }
                }
              },
              "templates": [
                {
                  "type": "primary-item",
                  "value": "Identificador #<%- properties.id.replace('lote_cidadao.', '') %>"
                },
                {
                  "type": "secondary-item",
                  "value": "SQL: <%- properties.cd_setor_fiscal %>-<%- properties.cd_quadra_fiscal %>-<%- properties.cd_lote %> <%- properties.cd_condominio %> <%- properties.nm_logradouro_completo ?? '-' %>"
                }
              ]
            }
          ]
        }
      ]
    }

    Este exemplo combina um mapa com uma lista de lotes, cada um com uma ação de clique para abrir detalhes.

Ação de Botão

O template button é um botão simples com qualquer ação.

  • Propósito: Renderizar um botão simples com rótulo e ação.
  • Propriedades Raiz:
PropriedadeDescriçãoPadrãoExemplo
typeIdentificador do template, deve ser "button".-"edit-polygon"
labelO texto que será exibido no botão na tela."Ação""edit-polygon"
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
actionUma função string que será disparada quando o botão for clicado.(data) => console.log("Do nothing");

Este template não será renderizado quando a visualização for para impressão

  • Exemplo:

    {
      "type": "button",
      "label": "Imprimir",
      "properties": { "action": "(data) => console.log('DO NOTHING');" },
    },

    Este exemplo renderiza um botão "Imprimir" que executa uma ação de log.

Mapa do Polígono

O template polygon-map renderiza um mapa não interativo com um polígono destacado, configurado com uma visualização inicial e propriedades visuais.

  • Propósito: Exibir a geometria de um polígono em um mapa.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do template, deve ser "polygon-map"."polygon-map"
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
initialViewStateFunção JavaScript que define a visualização inicial do mapa (longitude, latitude, zoom)."(data) => { const centroid = utils.calculateCenterId(data.geometry.coordinates[0]); return { longitude: centroid[0], latitude: centroid[1], zoom: 16.5, pitch: 0, bearing: 0 }; }"
polygonPropsFunção JavaScript que configura as propriedades visuais do polígono (cor, contorno)."(data) => ({ id: 'polygon-layer', data: [{ coordinates: data.geometry.coordinates }], pickable: false, stroked: true, filled: true, lineWidthMinPixels: 2, getPolygon: (d) => d.coordinates, getFillColor: [255, 165, 0, 100], getLineColor: [255, 140, 0] })"
  • Exemplo:

    {
      "type": "polygon-map",
      "properties": {
        "initialViewState": "(data) => { const centroid = utils.calculateCenterId(data.geometry.coordinates[0]); return { longitude: centroid[0], latitude: centroid[1], zoom: 16.5, pitch: 0, bearing: 0 }; }",
        "polygonProps": "(data) => ({ id: 'polygon-layer', data: [{ coordinates: data.geometry.coordinates }], pickable: false, stroked: true, filled: true, lineWidthMinPixels: 2, getPolygon: (d) => d.coordinates, getFillColor: [255, 165, 0, 100], getLineColor: [255, 140, 0] })"
      }
    }

    Este template exibe um polígono centralizado com um preenchimento laranja e um contorno mais escuro.

Item Primário

O template primary-item define o conteúdo primário de um item em uma lista (Itens de Lista), tipicamente usado para o título ou informação principal.

  • Propósito: Exibir o texto principal de um item de lista, como um identificador ou nome.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do template, deve ser "primary-item"."primary-item"
valueExpressão EJS que define o conteúdo primário do item."Identificador #<%- properties.id.replace('lote_cidadao.', '') %>"
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
(Nenhuma)Atualmente, não há propriedades específicas no objeto properties.-
  • Exemplo:

    {
      "type": "primary-item",
      "value": "Identificador #<%- properties.id.replace('lote_cidadao.', '') %>"
    }

    Este template exibe o identificador de um lote sem o prefixo "lote_cidadao.".

Item Secundário

O template secondary-item define o conteúdo secundário de um item em uma lista (Itens de Lista), tipicamente usado para detalhes suplementares.

  • Propósito: Exibir informações adicionais, como códigos ou descrições, em uma segunda linha.
  • Propriedades Raiz:
PropriedadeDescriçãoExemplo
typeIdentificador do template, deve ser "secondary-item"."secondary-item"
valueExpressão EJS que define o conteúdo secundário do item."SQL: <%- properties.cd_setor_fiscal %>-<%- properties.cd_quadra_fiscal %>-<%- properties.cd_lote %> <%- properties.cd_condominio %> <%- properties.nm_logradouro_completo ?? '-' %>"
  • Propriedades do Objeto properties:
PropriedadeDescriçãoExemplo
(Nenhuma)Atualmente, não há propriedades específicas no objeto properties.-
  • Exemplo:

    {
      "type": "secondary-item",
      "value": "SQL: <%- properties.cd_setor_fiscal %>-<%- properties.cd_quadra_fiscal %>-<%- properties.cd_lote %> <%- properties.cd_condominio %> <%- properties.nm_logradouro_completo ?? '-' %>"
    }

    Este template exibe códigos fiscais e o endereço de um lote.