Browse Source

Mapa de atuação do Interlegis

sigi-4.0
Sesóstris Vieira 6 days ago
parent
commit
8088908a3b
  1. 15
      sigi/apps/home/static/home/css/openmap.css
  2. 174
      sigi/apps/home/static/home/js/openmap.js
  3. 123
      sigi/apps/home/templates/home/mapfilter.html
  4. 87
      sigi/apps/home/templates/home/openmap.html
  5. 47
      sigi/apps/home/templates/home/openmapdetail.html
  6. 24
      sigi/apps/home/views.py

15
sigi/apps/home/static/home/css/openmap.css

@ -6,8 +6,13 @@ html,
width: 100%;
}
#content {
height: calc(100% - 60px);
#content,
.content {
height: 100%;
}
.main {
height: calc(100% - 80px);
}
.filterwrap {
@ -134,4 +139,10 @@ li>a.clear-filters>i.material-icons {
.embed-map-content {
height: 100% !important;
}
.ui-autocomplete {
overflow-y: auto;
overflow-x: hidden;
z-index: 1000;
}

174
sigi/apps/home/static/home/js/openmap.js

@ -0,0 +1,174 @@
const stadia_tiles_options = {
attribution:
`
&copy; <a href="https://www.stadiamaps.com/" target="_blank">Stadia Maps</a>
&copy; <a href="https://stamen.com/" target="_blank">Stamen Design</a>
&copy; <a href="https://openmaptiles.org/" target="_blank">OpenMapTiles</a>
&copy; <a href="https://www.openstreetmap.org/about/" target="_blank">OpenStreetMap contributors</a>"
`
};
var mymap;
var orgao_layer_group = L.layerGroup();
var map_center = [-14.235004, -51.92528];
var options = { color: 'blue', fillColor: 'red', fillOpacity: 0.4, radius: 500 };
var unfiltred_options = { color: 'red', fillColor: 'red', fillOpacity: 0, radius: 1000 };
$(document).ready(function () {
$("input[type=checkbox]").change(filtra);
var search_field = $("#map-search");
if (search_field) {
search_field.autocomplete({
source: search_field.attr("data-source"),
minLength: 3,
select: function (event, ui) {
map_fly_to(ui.item);
}
});
}
var base_layers = {
"Toner": L.tileLayer(
"https://tiles.stadiamaps.com/tiles/stamen_toner/{z}/{x}/{y}{r}.png",
stadia_tiles_options,
),
"Toner lite": L.tileLayer(
"https://tiles.stadiamaps.com/tiles/stamen_toner_lite/{z}/{x}/{y}{r}.png",
stadia_tiles_options,
),
"Terrain": L.tileLayer(
"https://tiles.stadiamaps.com/tiles/stamen_terrain/{z}/{x}/{y}{r}.png",
stadia_tiles_options,
),
"Water color": L.tileLayer(
"https://tiles.stadiamaps.com/tiles/stamen_watercolor/{z}/{x}/{y}.jpg",
stadia_tiles_options,
),
"Open street": L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: 'Map data and imagery &copy; <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, ',
tileSize: 512,
zoomOffset: -1
}),
};
mymap = L.map('map', {
center: map_center,
zoom: 4.5,
zoomSnap: 0.01,
layers: [base_layers.Terrain, orgao_layer_group]
});
mymap.zoomControl.options.zoomInTitle = "{% trans 'Aproximar' %}";
mymap.zoomControl.options.zoomOutTitle = "{% trans 'Afastar' %}";
mymap.zoomControl.setPosition("bottomright");
L.control.layers(base_layers).addTo(mymap);
mymap.on("popupopen", function (e) {
var popup = e.popup;
mark = popup._source;
$.ajax({
type: "GET",
url: window.__openmapdetail_prefix__.replace("changeme", mark.orgao_id),
encode: true
}).done(function (content) {
popup.setContent(content);
})
})
filtra();
function filtra() {
var name = $(this).attr("name"),
value = $(this).attr("value"),
checked = $(this).prop("checked");
if (name) {
$(`input[type=checkbox][name=${name}][value=${value}]`).prop("checked", checked);
}
if (value == "ignore") {
controls = $(this).attr("data-controls");
$("input[type=checkbox][name='" + controls + "']").prop("disabled", checked);
}
if (value == "none") {
$("input[type=checkbox][name='" + name + "'][value!='none']").prop("checked", !checked);
} else {
if (checked) {
$("input[type=checkbox][name='" + name + "'][value='none']").prop("checked", false);
}
}
if (name == 'regiao') {
$("input[type=checkbox][name='uf'][data-regiao='" + value + "']").prop('checked', checked);
}
if (name == 'uf') {
var sigla_regiao = $(this).attr('data-regiao'),
regiao = $("input[type=checkbox][value='" + sigla_regiao + "']");
if ($("input[type=checkbox][name='uf'][data-regiao='" + sigla_regiao + "']:checked").length == 0) {
$(regiao).prop('checked', false).prop("indeterminate", false);
} else if ($("input[type=checkbox][name='uf'][data-regiao='" + sigla_regiao + "']:not(:checked)").length == 0) {
$(regiao).prop('checked', true).prop("indeterminate", false);
} else {
$(regiao).prop("indeterminate", true)
}
}
var formData = $("#filterForm").serializeArray();
$("#filter-spinner").toggleClass("d-none d-flex");
$.ajax({
type: "GET",
url: window.__openmapdata__,
data: formData,
dataType: "json",
encode: true,
}).done(function (returnedData) {
$("#filter-spinner").toggleClass("d-none d-flex");
$("#totalOrgao").text(returnedData.length);
var new_data_layer = L.layerGroup();
returnedData.forEach(function (casa) {
if (casa[2] === null || casa[3] === null) {
alert(casa[1] + " está sem coordenadas geográficas e não será plotada");
} else {
var mark = L.circle([casa[2], casa[3]], options).bindTooltip(casa[1]).bindPopup('<div class="preloader-wrapper small active"><div class="spinner-layer spinner-green-only"><div class="circle-clipper left"><div class="circle"></div></div><div class="gap-patch"><div class="circle"></div></div><div class="circle-clipper right"><div class="circle"></div></div></div></div>').addTo(new_data_layer);
mark.orgao_id = casa[0]
}
});
orgao_layer_group.clearLayers();
orgao_layer_group.addLayer(new_data_layer);
})
}
$("#clear-filters").click(function (event) {
event.preventDefault();
$("input[type=checkbox][name=tipo_orgao]").prop('checked', false);
$("input[type=checkbox][name=tipo_servico]").prop('checked', false);
$("input[type=checkbox][name=tipo_convenio]").prop('checked', false);
$("input[type=checkbox][name=regiao]").prop('checked', false);
$("input[type=checkbox][name=uf]").prop('checked', false);
$("input[type=checkbox][name=gerente]").prop('checked', false);
filtra();
});
$("#center-map").click(function (event) {
event.preventDefault();
mymap.flyTo(map_center, 4.5);
});
});
function map_fly_to(obj) {
mymap.flyTo([obj.lat, obj.lng], 8.5);
var encontrado = false;
mymap.eachLayer(function (layer) {
if (layer instanceof L.Circle) {
if (layer.orgao_id == obj.id) {
layer.openPopup();
encontrado = true;
}
}
});
if (!encontrado) {
var mark = L.circle([obj.lat, obj.lng], unfiltred_options).bindTooltip(obj.label).bindPopup("").addTo(mymap);
mark.orgao_id = obj.id
mark.openPopup();
}
}

123
sigi/apps/home/templates/home/mapfilter.html

@ -0,0 +1,123 @@
{% load i18n %}
<form id="filterForm" action="" method="get">{{ csrftoken }}
<div class="accordion" id="accordionMapFilter">
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseOrgao" aria-expanded="false" aria-controls="collapseOrgao">
{% trans "Órgão" %}
</button>
</h2>
<div id="collapseOrgao" class="accordion-collapse collapse">
<div class="accordion-body">
{% for o in tipos_orgao %}
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_orgao_{{ o.sigla }}" name="tipo_orgao" value="{{ o.sigla }}"{% if o.sigla in pre_tipos_orgao or not pre_tipos_orgao %} checked{% endif %}/>
<label class="form-check-label" for="tipo_orgao_{{ o.sigla }}">{{ o.nome }}</label>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseServico" aria-expanded="false" aria-controls="collapseServico">
{% trans "Serviço" %}
</button>
</h2>
<div id="collapseServico" class="accordion-collapse collapse">
<div class="accordion-body">
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_servico_ignore" name="ignore_tipo_servico" value="ignore" data-controls="tipo_servico"{% if 'ignore' in pre_tipos_servico %} checked{% endif %} />
<label class="form-check-label" for="tipo_servico_ignore">{% trans "Ignorar" %}</label>
</div>
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_servico_none" name="tipo_servico" value="none"{% if 'none' in pre_tipos_servico %} checked{% endif %} />
<label class="form-check-label" for="tipo_servico_none">{% trans "Nenhum serviço" %}</label>
</div>
{% for s in tipos_servico %}
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_servico_{{ s.sigla }}" name="tipo_servico" value="{{ s.sigla }}"{% if s.sigla in pre_tipos_servico or not pre_tipos_servico %} checked{% endif %} />
<label class="form-check-label" for="tipo_servico_{{ s.sigla }}">{{ s.nome }}</label>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseConvenio" aria-expanded="false" aria-controls="collapseConvenio">
{% trans "Convênio" %}
</button>
</h2>
<div id="collapseConvenio" class="accordion-collapse collapse">
<div class="accordion-body">
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_convenio_ignore" name="ignore_tipo_convenio" value="ignore" data-controls="tipo_convenio"{% if 'ignore' in pre_tipos_convenio %} checked{% endif %} />
<label class="form-check-label" for="tipo_convenio_ignore">{% trans "Ignorar" %}</label>
</div>
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_convenio_none" name="tipo_convenio" value="none"{% if "none" in pre_tipos_convenio %} checked{% endif %} />
<label class="form-check-label" for="tipo_convenio_none">{% trans "Sem convênio" %}</label>
</div>
{% for c in tipos_convenio %}
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="tipo_convenio_{{ c.sigla }}" name="tipo_convenio" value="{{ c.sigla }}"{% if c.sigla in pre_tipos_convenio or not pre_tipos_convenio %} checked{% endif %}/>
<label class="form-check-label" for="tipo_convenio_{{ c.sigla }}">{{ c.nome }}</label>
</div>
{% endfor %}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseRegiao" aria-expanded="false" aria-controls="collapseRegiao">
{% trans "Região / estados" %}
</button>
</h2>
<div id="collapseRegiao" class="accordion-collapse collapse">
<div class="accordion-body">
{% for s, n, ufs in regioes %}
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="regiao_{{ s }}" name="regiao" value="{{ s }}" />
<label class="form-check-label" for="regiao_{{ s }}">{{ n }}</label>
</div>
<div class="ps-3">
{% for uf in ufs %}
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="uf_{{ uf.sigla }}" name="uf" value="{{ uf.sigla }}" data-regiao="{{ s }}" {% if uf.sigla in pre_ufs %} checked{% endif %}/>
<label class="form-check-label" for="uf_{{ uf.sigla }}">{{ uf.nome }}</label>
</div>
{% endfor %}
</div>
{% endfor %}
</div>
</div>
</div>
<div class="accordion-item">
<h2 class="accordion-header">
<button class="accordion-button collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#collapseGerente" aria-expanded="false" aria-controls="collapseGerente">
{% trans "Gerente" %}
</button>
</h2>
<div id="collapseGerente" class="accordion-collapse collapse">
<div class="accordion-body">
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="gerente_ignore" name="gerente_ignore" value="ignore" data-controls="gerente"{% if "ignore" in pre_gerentes %} checked{% endif %} />
<label class="form-check-label" for="gerente_ignore">{% trans "Ignorar" %}</label>
</div>
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="gerente_none" name="gerente" value="none" {% if "none" in pre_gerentes %} checked{% endif %}/>
<label class="form-check-label" for="gerente_none">{% trans "Sem gerente" %}</label>
</div>
{% for g in gerentes %}
<div class="form-check{% if djbs.CHECK_AS_SWITCH %} form-switch{% endif %}"{% if djbs.CHECK_AS_SWITCH %} role="switch"{% endif %}>
<input class="form-check-input" type="checkbox" form="filterForm" id="gerente_{{ g.id|stringformat:"s" }}" name="gerente" value="{{ g.id|stringformat:"s" }}" {% if g.id|stringformat:"s" in pre_gerentes or not pre_gerentes%} checked{% endif %}/>
<label class="form-check-label" for="gerente_{{ g.id|stringformat:"s" }}">{{ g.nome_completo }}</label>
</div>
{% endfor %}
</div>
</div>
</div>
</div>
</form>

87
sigi/apps/home/templates/home/openmap.html

@ -0,0 +1,87 @@
{% extends "base.html" %}
{% load static i18n djbs_extras %}
{% block extrastyle %}
{{ block.super }}
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css"
integrity="sha256-p4NxAoJBhIIN+hmNHrzRCf9tD/miZyoHS5obTRR9BMY="
crossorigin=""/>
<link rel="stylesheet" href="https://code.jquery.com/ui/1.14.1/themes/base/jquery-ui.css">
<link rel="stylesheet" href="{% static 'home/css/openmap.css' %}" />
{% endblock %}
{% block extrahead %}
{{ block.super }}
<script src="https://code.jquery.com/ui/1.14.1/jquery-ui.js"></script>
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<meta http-equiv="Content-Type" content="text/xhtml; charset=UTF-8" />
<meta name="robots" content="NONE,NOARCHIVE" />
<meta name="referrer" content="strict-origin-when-cross-origin"/>
<script type="text/javascript">
//<![CDATA[
window.__admin_media_prefix__ = "{% filter escapejs %}{% static "admin / " %}{% endfilter %}";
window.__openmapdetail_prefix__ = "{% url 'openmapdetail' 'changeme' %}";
window.__openmapdata__ = "{% url 'openmapdata' %}";
//]]>
</script>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"
integrity="sha256-20nQCchB9co0qIjJZRGuk2/Z9VM+kNiyxNV1lvTlZBo="
crossorigin=""></script>
<script type="text/javascript" src="{% static 'js/search.js' %}"></script>
<script type="text/javascript" src="{% static 'home/js/openmap.js' %}"></script>
{% endblock %}
{% block nav-breadcrumbs %}{% endblock %}
{% block title %}{% if subtitle %}{{ subtitle }} | {% endif %}{{ title }} | {{ site_title|default:_('Django site admin') }}{% endblock %}
{% block navbar-branding %}
<a class="navbar-brand" href="{% if user.is_staff %}{% url 'admin:index' %}{% else %}{% url "openmap" %}{% endif %}">
<img src="{% static 'img/logo.svg' %}" height="48px"/>
<span class="sidenav-collapsible collapse collapse-horizontal show fw-bold ms-2">{{ site_header|default:_('Django administration') }}</span>
</a>
{{ block.super }}
{% endblock %}
{% block toolclass %}{{ block.super }} ms-auto{% endblock %}
{% block nav-global %}
<a id="center-map" class="nav-link" href="#" title="{% trans 'Centralizar o mapa' %}">{% icon "map-cross" %} {% trans 'Centralizar o mapa' %}</a>
<a class="nav-link" data-bs-toggle="offcanvas" href="#sidebar-offcanvas" title="{% trans "Filtrar dados" %}">{% icon "filter" %} {% trans "Filtrar dados" %}</a>
<a id="clear-filters" class="nav-link" href="#" title="{% trans 'Remover todos os filtros' %}">{% icon "error" %} {% trans 'Remover todos os filtros' %}</a>
<a id="" class="nav-link" href="{% url 'admin:index' %}" title="{% trans "Login" %}">{% icon "login" %} {% trans "Login" %}</a>
{% endblock %}
{% block side_nav %}{% endblock %}
{% block menu %}{% endblock %}
{% block coltype %}py-2{% endblock %}
{% block search %}
<form id="searchform" class="d-flex gap-2 ms-2" role="search">
{% icon "search" "d-none d-lg-inline-block navbar-text" %}
<input id="map-search" type="search" class="form-control" placeholder="{% trans "Procurar" %}" aria-label="{% trans "Procurar" %}" data-source="{% url 'openmapsearch' %}">
</form>
{% endblock search %}
{% block content %}
<div id="filter-spinner" class="fixed-top vh-100 bg-secondary bg-opacity-50 d-none flex-wrap justify-content-center align-content-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">{% trans "Loading..." %}</span>
</div>
</div>
<div id="map">
<!-- open street map -->
</div>
{% endblock %}
{% block sidebar-title %}{% translate "Filtrar" %}{% endblock sidebar-title %}
{% block sidebar-body %}
<div class="mb-3 text-body-secondary d-flex">
<small class="me-auto"><strong>Total de Órgãos selecionados: </strong></small>
<span class="badge text-bg-info" id="totalOrgao">-</span>
</div>
{% include "home/mapfilter.html" %}
{% endblock sidebar-body %}

47
sigi/apps/home/templates/home/openmapdetail.html

@ -0,0 +1,47 @@
<table class="table table-sm table-striped">
<tr>
<th colspan="2">
{% if user.is_staff %}
{% url "admin:casas_orgao_change" orgao.id as orgao_url %}
{% else %}
{% url "servicos_casas_atendidas" orgao.id as orgao_url %}
{% endif %}
<a href="{{ orgao_url }}" target="_blank">
{{ orgao }}
</a>
</th>
</tr>
<tr><th>Endereço</th><td><address>{{ orgao.logradouro }}, {{ orgao.bairro }}, {{ orgao.municipio.nome }}, {{ orgao.municipio.uf.sigla }}, CEP: {{ orgao.cep }}</address></td></tr>
{% if telefones %}
<tr>
<th>Telefones</th>
<td>
{% for telefone in telefones %}
<a href="tel:{{ telefone }}">{{ telefone }}</a>
{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if orgao.email %}<tr><th>E-mail</th><td><a href="mailto:{{ orgao.email }}">{{ orgao.email }}</a></td></tr>{% endif %}
{% if orgao.convenio_set.all %}
<tr>
<th>Convênios</th>
<td>
{% for c in orgao.convenio_set.all %}
{% if user.is_staff %}
<a href="{% url 'admin:convenios_convenio_change' c.id %}" target="_blank">
{{ c.projeto.sigla }} ({{ c.get_status }})
</a>
{% else %}
{{ c.projeto.sigla }} ({{ c.get_status }})
{% endif %}
{% if not forloop.last %}, {% endif %}
{% endfor %}
</td>
</tr>
{% endif %}
{% if orgao.servico_set.all %}
<tr><th>Serviços</th><td>{% for s in servicos.all %}{% if s.url %}<a href="{% if '//' not in s.url %}//{% endif %}{{ s.url }}" target="_blank">{{ s }}</a>{% else %}{{ s }}{% endif %}{% if not forloop.last %}, {% endif %}{% endfor %}</td></tr>
{% endif %}
</table>

24
sigi/apps/home/views.py

@ -346,28 +346,26 @@ def openmapdetail(request, orgao_id):
def openmapsearch(request):
q = request.GET.get("q", "")
q = request.GET.get("q", request.GET.get("term", ""))
if len(q) < 3:
return JsonResponse({"result": "unsearchable"})
dados = Orgao.objects.filter(
tipo__legislativo=True, search_text__icontains=to_ascii(q)
)[:10]
dados = dados.values(
"id",
"nome",
"municipio__uf__sigla",
"municipio__latitude",
"municipio__longitude",
)
dados = [
{
"id": d["id"],
"label": f"{d['nome']} - {d['municipio__uf__sigla']}",
"label": f"{d['nome']}, {d['municipio__uf__sigla']}",
"lat": d["municipio__latitude"],
"lng": d["municipio__longitude"],
}
for d in dados
for d in Orgao.objects.filter(
tipo__legislativo=True, search_text__icontains=to_ascii(q)
)[:10].values(
"id",
"nome",
"municipio__uf__sigla",
"municipio__latitude",
"municipio__longitude",
)
]
return JsonResponse(list(dados), safe=False)

Loading…
Cancel
Save