Desenvolvendo um SIG para Suporte de Decisões Municipais #2
Olá pessoal, boa noite.
No post anterior falamos sobre a modelagem de um sistema que atendesse as necessidades de uma prefeitura e mostramos como modelar a parte de sistema viário. O sistema viário foi modelado primeiro pois ele é de fato, o coração de uma cidade e muita coisa acontence "em torno" do sistema viário.
Estamos assumindo com nosso modelo, que construíremos um sistema de geocoding, capaz de localizar endereços em nossa base de logradouros. Através deste sistema construíremos localizadores de diversos tipos de feição, como acidentes de trânsito, pontos de ônibus, focos de dengue, entre outras.
Hoje iremos conversar sobre a modelagem do sistema de transportes. Obviamente é um modelo simples, que não atende todas as necessidades de uma empresa/prefeitura para administrar o sistema de transporte público, mas já ajuda. Bem, do que é feito um sistema de transporte público? Falaremos aqui somente de linhas, pontos de parada, sentidos (bairro-centro, centro-bairro) e horários. Poderíamos muito bem falar de localização em tempo real, relatórios de viagem, etc., mas foge um pouco do objetivo da série - de ser uma introdução à modelagem de um sistema completo.
2.2 - Sistema de Transporte Público
Vamos começar com a modelagem do nosso sistema de Transporte. Vamos modelar Pontos de Parada, Linhas, Sentidos, e Horários.
CREATE TABLE tp_ponto_parada
(
id_tp_ponto_parada serial not null,
dc_tp_ponto_parada varchar(50) not null,
constraint tp_ponto_parada_pk PRIMARY KEY (id_tp_ponto_parada),
constraint tp_ponto_parada_un UNIQUE (dc_tp_ponto_parada)
);
CREATE TABLE ponto_parada
(
id_ponto_parada serial not null,
dc_ponto_parada varchar(255) not null, -- descricao do ponto
tp_ponto_parada integer not null references tp_ponto_parada (id_tp_ponto_parada),
constraint ponto_parada_pk PRIMARY KEY (id_ponto)
);
-- vamos adicionar nossa coluna geométrica a ponto_parada
SELECT * FROM AddGeometryColumn('public','ponto_parada','the_geom',-1,'POINT',2);
CREATE TABLE linha
(
id_linha serial not null,
nm_linha varchar(50) not null,
dc_linha varchar(255) not null,
constraint linha_pk PRIMARY KEY (id_linha),
constraint linha_nm_linha_un UNIQUE (nm_linha) -- o nosso nome de linha deve ser único.
);
Bem, modelos nossas entidades básicas, pontos e linhas. Mas como descobrir qual linha serve à cada ponto? Ou como dizer se um ponto de parada pertence à determinada linha? Temos neste momento, uma relação de muitos para muitos. Porque? Bem, um ponto pode servir à mais de uma linha. E uma linha, com certeza abrange vários pontos. Para isto ser feito de maneira correta, temos de criar uma entidade intermediária, decompondo nosso relacionamento em 1:M, de cada um dos lados.
CREATE TABLE sentido
(
id_sentido serial not null,
dc_sentido varchar(50) not null,
constraint sentido_pk primary key (id_sentido)
);
-- vamos aproveitar e inserir os sentidos possíveis!
INSERT INTO sentido(id_sentido,dc_sentido) VALUES (DEFAULT,'CENTRO - BAIRRO');
INSERT INTO sentido(id_sentido,dc_sentido) VALUES (DEFAULT,'BAIRRO - CENTRO');
CREATE TABLE linha_possui_ponto
(
id_linha integer not null references linha (id_linha),
id_ponto_parada integer not null references ponto_parada (id_ponto_parada),
id_sentido integer not null references sentido (id_sentido),
constraint lpp_pk primary key (id_linha,id_ponto_parada,id_sentido)
);
Através desta tabela intermediária, conseguimos modelar quem pertence à quem. Podem ver, que as chaves estrangeiras em linha_possui_ponto, não permite o cadastro de linhas inexistentes ou de pontos inexistentes, nem de pontos duplicados que partilhem do mesmo sentido. Desta forma, podemos construir um cadastro básico de linhas e pontos, bem como de quais pontos são servidos por quais linhas.
Agora, precisamos de uma maneira inteligente de desenhar as linhas de ônibus que operam na cidade fictícia. Precisamos criar algo com uma coluna geométrica, correto? Errado. Como temos uma tabela geométrica com nossos logradouros, podemos facilmente utilizá-la para construir nossos trajetos de linha.
Por que fazer desta maneira?
- Não repita informação.
- Não repita informação.
- Não repita informação.
É um motivo forte o bastante. Imagine se temos milhares de linhas, com milhares de trechos servidos. Se alterarmos nossos logradouros, teremos de alterar também nossas linhas - configurando um problema em potencial. Por isso iremos utilizar as geometrias da tabela logradouros para desenharmos nossas linhas.
Esta tabela tomará a forma de uma tabela intermediária, informando ordem e sentido. Desta maneira, utilizando uma view construíremos nossa tabela virtual de linhas.
CREATE TABLE linha_possui_trecho
(
id_lpt serial not null,
id_linha integer not null references linha (id_linha),
id_trecho_logradouro not null references trecho_logradouro (id_trecho_logradouro),
id_sentido not null references sentido (id_sentido),
ordem integer not null default 0,
constraint lpt_pk primary key (id_lpt),
constraint lpt_un_linha_lpt unique (id_linha,id_trecho_logradouro,id_sentido,ordem)
);
Através desta tabela intermediária, conseguimos informar à nosso sistema quais são os trechos de logradouro que formam uma linha específica. Realmente é algo à mais para monitorarmos e inserir no banco de dados, mas atualmente é a única maneira. A idéia aqui é construir uma ferramenta específica, que seleciona trechos de logradouros, e em memória armazena estas informações, bem como sua ordem (de seleção). Após a seleção, o ideal é o usuário executar o comando de "inserir", e nosso sistema, cuide do restante para o mesmo. Nada de editar tabelas como esta na mão.
Já temos nosso modelo preliminar, mas como vamos mostrar as linhas em um cliente web? Necessitamos gerar as linhas em tempo de execução, com as informações pertinentes. Para isto, iremos utilizar uma view e adicionar os registros apropriados à tabela geometry_columns para que esta fique transparente para o usuário/serviço de mapas.
CREATE OR REPLACE VIEW linhas_transporte_publico AS SELECT id_linha, nm_linha, ST_Collect((SELECT the_geom from logradouros where gid = id_trecho_logradouro)) as "the_geom" FROM linha LEFT OUTER JOIN linha_possui_trecho ON (linha.id_linha = linha_possui_trecho.id_linha) LEFT OUTER JOIN trecho_logradouro ON (linha_possui_trecho.id_trecho_logradouro = trecho_logradouro.id_trecho_logradouro) GROUP BY id_linha,nm_linha
Esta view nos possibilitará disponibilizar as informações de linha como um todo, sem diversos registros. O segredo está na função ST_Collect, responsável por unir os trechos de logradouros em nossa tabela trecho_logradouro. Veja que não estamos duplicando informação, apenas reutilizando os dados já existentes em nosso modelo.
Existem outras tabelas interessantes em se disponibilizar, tais como pontos de táxi, linhas de metrô/trem, transportes marítimos, aeroportos, terminais de embarque, etc. Deixo este exercício para o leitor. O objetivo aqui é mostrar como a modelagem de dados preliminar do sistema viário é importante e como podemos simplificar nosso trabalho futuro.
No próximo artigo falaremos sobre setores administrativos, bairros, quadras, lotes, etc. Estes dados são muito importantes para a administração pública e com certeza devem estar presentes em nosso sisteminha.
O que acharam?
Abraços
Related posts:
julho 29th, 2010 - 14:06
George, muito bom o post… como sempre, abraço amigo!
[Translate]
julho 31st, 2010 - 00:09
Paulo Vitor! Muito obrigado pela força. Continue acompanhando que ainda temos algumas partes deste artigo!
Abraços!
[Translate]
agosto 13th, 2010 - 19:00
Muito bom mesmo, e estou ansioso pelas continuações.
[Translate]
outubro 9th, 2010 - 12:32
Amigo, os posts dessa série estão muito bons. Espero que os próximos possam vir logo!
[Translate]
outubro 10th, 2010 - 19:58
Olá Rodrigo, obrigado pela força. Vou continuar a série sim, mas me dê um tempinho!
Abraços
[Translate]
janeiro 18th, 2011 - 13:48
George muito bom seu artigo, mas e a continuação? Estou no aguardo da continuação da matéira.
Agraços!
[Translate]
janeiro 28th, 2011 - 22:26
Olá Daniel, tudo bom? Ultimamente tem sido uma correria, mas prometo que este final de semana dou um jeito de continuar com o artigo.
Peço desculpas aos leitores por esperarem o(s) artigo(s).
Abraços
[Translate]
fevereiro 10th, 2011 - 15:04
george,mto bom post. COntinue com eles…
[Translate]
fevereiro 11th, 2011 - 20:51
Obrigado Fabrício! Continue nos visitando!
[Translate]