quarta-feira, dezembro 22, 2010

Análise Espacial com o PostGIS e o GvSIG

Uma das inúmeras vantagens de se trabalhar com o PostGIS é a possibilidade de criar views das análises espaciais, tornando os resultados das análises dinâmicas em função dos dados de entrada utilizados. Desta forma também evita-se a criação de novo layers em função de uma atualização da base.

Para exemplificar, vamos imaginar a seguinte situação: em uma determinada cidade foi definido que a área de influência de uma parada de ônibus é de 40 metros e que a prefeitura deve identificar quais são os lotes dentro de cada área de influência das paradas. Apesar da situação ser muito comum para quem já trabalha com SIG, surge a necessidade de se repetir a análise a cada nova parada inserida ou excluída no sistema, o que pode acabar gerando inconsistências na base em função do mal gerenciamento destes arquivos.

Com o uso do PostGIS e do GvSIG eu vou mostrar como resolver este problema sem a necessidade de ficar criando novas camadas a partir das análises desse problema, no caso, a criação do buffer de 40 metros e a consulta espacial (intersects).

No PgAdmin 3, crie um novo Banco de Dados Espacial com o nome cadastro (Figura 1), em seguida, baixe, descompacte e carregue no banco criado o arquivo SQL do link abaixo (Figura 2):

Figura 1 - Criação do Banco de Dados Espacial

Figura 2 - Carregando o Script SQL da tabela Lote


Agora vamos criar a tabela ponto_onibus a partir do script abaixo (Figura 3):
CREATE TABLE ponto_onibus(
 id serial PRIMARY KEY,
 observacao varchar(50)
 );

SELECT AddGeometryColumn (
 'public', -- esquema
 'ponto_onibus', -- nome da tabela
 'the_geom', -- nome da coluna
 '29185', -- SRID
 'POINT', -- tipo de geometria
 2 -- plano (2d)
 );
 


Figura 3 - Criação da tabela ponto_onibus no PgAdmin

Vamos criar duas views, uma para os buffers de 40 metros em torno das paradas de ônibus e a outra dos os lotes que estão dentro desta distância (função intersects).

-- Criando a view buffer_ponto_onibus
CREATE OR REPLACE VIEW buffer_ponto_onibus AS
SELECT id, buffer(the_geom, 40) AS the_geom 
FROM ponto_onibus;

-- Registrando a view na tabela de metadados geometry_columns
INSERT INTO geometry_columns(f_table_catalog, f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, "type")
VALUES ('', 'public', 'buffer_ponto_onibus', 'the_geom', 2, 29185, 'POLYGON');

-- Criando a view lotes_ponto_onibus
CREATE OR REPLACE VIEW lotes_ponto_onibus AS
SELECT l.*
FROM lote l,  buffer_ponto_onibus b
WHERE intersects(l.the_geom, b.the_geom);

-- Registrando a view na tabela de metadados geometry_columns
INSERT INTO geometry_columns(f_table_catalog, f_table_schema, f_table_name, f_geometry_column, coord_dimension, srid, "type")
VALUES ('', 'public', 'lotes_ponto_onibus', 'the_geom', 2, 29185, 'POLYGON');
 

Abra o GvSIG e mude a projeção do bloco para 29185 (Figura 4)

Figura 4 - Troca do sistema de projeção no GvSIG

Com o bloco aberto, clique no botão adicionar plano de informação e escolha a segunda aba (GeoDB), em seguida configure a conexão com o banco de dados "cadastro" (Figura 5).

Figura 5 - Configuração da conexão com o Banco de Dados

Configurada a conexão, adicione as tabelas "lote" e "parada_onibus" e as views "buffer_ponto_onibus" e "lotes_ponto_onibus" (Figura 6).

Figura 6 - Adicionando as camadas do BD

Inicie a edição da camada ponto_onibus clicando com o botão direito em cima desta camada e escolhendo a opção "Iniciar Edição" em seguida, adicione algumas paradas de forma aleatória. Termine a edição Salvando as alterações (Figura 7).

Figura 7 - Inserindo os pontos de ônibus
Para ver as modificações nas dias views, basta desligar e ligar uma das camadas  "buffer_ponto_onibus" ou "lotes_ponto_onibus" (Figura 8).

Figura 8 - Resultado dinâmico das análises a partir dos pontos criados

A cada novo ponto criado ou excluído, as duas views são atualizadas automaticamente (Figura 9).

Figura 9 - Resultado das análises após modificar a camada ponto_onibus

Esperam que tenham gostado dessa dica, até a próxima!

sexta-feira, dezembro 17, 2010

Alternativa Open Source para o ArcCatalog e o ArcToolbox

O wxGIS possui as mesmas características dos módulos ArcCatalog e ArcToolBox do ArcGIS, e o que é melhor, é open source :)





Para instalar o wxGIS no Ubuntu é necessário:

sábado, dezembro 04, 2010

PostrgeSQL - dados para realização dos exercícios



Baixe o arquivo neste link.

CREATE TABLE municipio (
idmun serial PRIMARY KEY,
nomemun varchar(50),
populacao int
);

CREATE TABLE proprietario (
idprop serial PRIMARY KEY,
cpf char(14),
nomeprop varchar(50),
telprop char(11)
);

CREATE TABLE fazenda (
idfaz serial PRIMARY KEY,
nomefaz varchar(50),
areafaz_ha numeric(8,2),
idmun int,
idprop int,
FOREIGN KEY(idmun) REFERENCES municipio (idmun),
FOREIGN KEY(idprop) REFERENCES proprietario (idprop)
);

CREATE TABLE variedade (
idvar serial PRIMARY KEY,
nomevar varchar(60)
);

CREATE TABLE plantacao (
idplan serial PRIMARY KEY,
plantio date,
colheita date,
areaplan_ha numeric(10,0),
idfaz int,
idvar int,
FOREIGN KEY(idfaz) REFERENCES fazenda (idfaz),
FOREIGN KEY(idvar) REFERENCES variedade (idvar)
);

-------------- INSERTS --------------
-------------------------------------

--TABELA municipio:
INSERT INTO municipio (nomemun, populacao) VALUES ('Patos', 100674);
INSERT INTO municipio (nomemun, populacao) VALUES ('Sousa', 65803);
INSERT INTO municipio (nomemun, populacao) VALUES ('Sapé', 50143);
INSERT INTO municipio (nomemun, populacao) VALUES ('Esperança', 31095);
INSERT INTO municipio (nomemun, populacao) VALUES ('Areia', 23829);
INSERT INTO municipio (nomemun, populacao) VALUES ('Bananeiras', 21851);

--TABELA proprietario
INSERT INTO proprietario (cpf, nomeprop,telprop ) VALUES ('027.424.624-91', 'João',    '83-87220485');
INSERT INTO proprietario (cpf, nomeprop,telprop ) VALUES ('011.122.234-51', 'Maria',   '83-99786654');
INSERT INTO proprietario (cpf, nomeprop,telprop ) VALUES ('123.455.009-98', 'Marcos',  '83-91230099');
INSERT INTO proprietario (cpf, nomeprop,telprop ) VALUES ('230.191.102-33', 'Rodrigo', '83-88998832');
INSERT INTO proprietario (cpf, nomeprop,telprop ) VALUES ('927.474.624-25', 'Carlos',  '83-88654321');

--TABELA fazenda
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Riacho Bonito',910, 1, 1);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Alegria',234, 2, 2);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Vida Bela',455, 3, 2);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Cascavel',1021, 6, 4);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Boquim',125, 4, 3);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Boi Só',782, 5, 5);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Giasa',1002, 2, 4);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Belo Monte',2304, 1, 2);
INSERT INTO fazenda (nomefaz, areafaz_ha, idmun, idprop ) VALUES ('Azulão', 234, 2, 2);

--TABELA variedade
INSERT INTO variedade (nomevar) VALUES ('Feijão');
INSERT INTO variedade (nomevar) VALUES ('Milho');
INSERT INTO variedade (nomevar) VALUES ('Cana de Açúcar');
INSERT INTO variedade (nomevar) VALUES ('Abacaxi');
INSERT INTO variedade (nomevar) VALUES ('Tomate');

--TABELA plantacao
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-08-10', '2013-01-02', 200, 1, 1);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-04-05', '2013-03-01', 520, 1, 2);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-11-30', '2013-02-24', 45, 2, 3);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-07-26', '2013-04-04', 20, 2, 4);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-06-05', '2013-01-02', 20, 3, 5);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-02-01', '2013-04-15', 900, 4, 3);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-04-23', '2012-11-21', 150, 6, 2);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-07-01', '2012-12-12', 120, 6, 3);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-11-20', '2013-02-10', 56, 8, 2);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-04-01', '2013-06-02', 590, 8, 3);
INSERT INTO plantacao (plantio, colheita, areaplan_ha, idfaz, idvar) VALUES ('2012-06-01', '2013-05-12', 345, 8, 4);

segunda-feira, outubro 11, 2010

Tutorial CodeIgniter - Parte 5

Vamos finalizar a nossa aplicação desenvolvendo o CRUD para a tabela rio, grande parte do código utilizado nas postagens anteriores será reutilizado.

Vamos começar com o rio_model.php:

<?php
class Rio_model extends CI_Model
{

    function __construct()
    {
        parent::__construct();
    }

    function get_rio()
    {
        $this->db->order_by('nome_rio', 'asc');
        //junção com a tabela bacia_hidrografica
        $this->db->join('bacia_hidrografica', 'bacia_hidrografica.id_bacia = rio.id_bacia');
        $query = $this->db->get('rio');
        if ($query->num_rows() > 0)
        {
            return $query->result();
        }
        else
        {
            return false;
        }
    }

    function insert()
    {
        $data = $_POST;
        return $this->db->insert('rio', $data);
    }

    function get_where($id)
    {
        $this->db->where('id_rio', $id);
        $this->db->join('bacia_hidrografica', 'bacia_hidrografica.id_bacia = rio.id_bacia');
        $query = $this->db->get('rio');
        return $query->row();
    }

    function update($id)
    {
        $data = $_POST;
        $this->db->where('id_rio', $id);
        $this->db->update('rio', $data);
    }

    function delete($id)
    {
        $this->db->where('id_rio', $id);
        return $this->db->delete('rio');
    }

}

O nosso controllador rio.php vai carregar dois model´s, já que teremos nos formulários de inserção e de atualização uma lista das bacias cadastradas:

<?php

class Rio extends CI_Controller
{

    function __construct()
    {
        parent::__construct();
        $this->load->model(array('rio_model', 'bacia_model'));
        $this->load->library('form_validation');
    }

    function index()
    {
        $data['titulo'] = "SIRH";
        $data['cabecalho'] = "Rios";

        $data['rios'] = $this->rio_model->get_rio();

        $this->load->view('rio_view', $data);
    }

    function criar()
    {
        $data['titulo'] = "SIRH - Novo Rio";
        $data['cabecalho'] = "Criar novo Rio";

        $data['bacias'] = $this->bacia_model->get_bacia();

        $this->load->view('rio_new', $data);
    }

    function insere()
    {

        $data['titulo'] = "SIRH - Novo Rio | Erro na Validação, preencha corretamente!";
        $data['cabecalho'] = "Criar novo Rio | Preencha Corretamente!";

        $data['bacias'] = $this->bacia_model->get_bacia();

        $this->form_validation->set_rules('nome_rio', 'Nome do Rio', 'required|alpha');
        $this->form_validation->set_rules('comprimento_km', 'Comprimento', 'required|numeric');
        $this->form_validation->set_rules('id_bacia', 'Bacia Hidrográfica', 'required');

        if ($this->form_validation->run() == FALSE)
        {
            $this->load->view('rio_new', $data);
        }
        else
        {
            $this->rio_model->insert();
            $this->session->set_flashdata('msg', 'Registro Criado com Sucesso!');
            redirect('rio');
        }
    }

    function editar($id)
    {
        $data['titulo'] = "SIRH - Editar Rio";
        $data['cabecalho'] = "Editar Rio";

        $data['registro'] = $this->rio_model->get_where($id);

        $data['bacias'] = $this->bacia_model->get_bacia();

        $this->load->view('rio_new', $data);
    }

    function atualizar()
    {
        $id = $this->input->post('id_rio');

        $data['titulo'] = "SIRH - Editar Rio | Erro na Validação, preencha corretamente!";
        $data['cabecalho'] = "Editar Rio | Preencha Corretamente!";

        $data['registro'] = $this->rio_model->get_where($id);

        $data['bacias'] = $this->bacia_model->get_bacia();

        $this->form_validation->set_rules('nome_rio', 'Nome do Rio', 'required|alpha');
        $this->form_validation->set_rules('comprimento_km', 'Comprimento', 'required|numeric');
        $this->form_validation->set_rules('id_bacia', 'Bacia Hidrográfica', 'required');

        if ($this->form_validation->run() == FALSE)
        {
            $this->load->view('rio_new', $data);
        }
        else
        {
            $this->rio_model->update($id);
            $this->session->set_flashdata('msg', 'Registro Atualizado com Sucesso!');
            redirect('rio');
        }
    }

    function deletar($id)
    {
        if ($this->rio_model->delete($id))
        {
            $this->session->set_flashdata('msg', 'Registro Deletado com Sucesso!');
        } else
        {
            $this->session->set_flashdata('msg', 'ERRO - Não foi possível deletar este registro!');
        }
        redirect('rio');
    }

}

A página de exibição dos rios (rio_view.php):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?= $titulo ?></title>
    </head>

    <body>
        <h2><?= $cabecalho ?></h2>

        <p><?= anchor('bacia', 'Ver Bacias Hidrográficas')?> &nbsp; <?= anchor('rio', 'Ver Rios')?></p>

        <p><?= anchor('rio/criar', 'Criar Rio') ?></p>

        <p><?= @$this->session->flashdata('msg'); ?></p>

        <table border="1">
            <thead>
                <tr>
                    <th>
                        Nome do rio
                    </th>
                    <th>
                        Comprimento (km)
                    </th>
                    <th>
                        Nome da bacia
                    </th>
                    <th>
                        Ações
                    </th>
                </tr>
            </thead>
            <tbody>
                <?php if ($rios != false): ?>
                <?php foreach ($rios as $rio): ?>
                        <tr>
                           <td>
                                <?= $rio->nome_rio ?>
                           </td>
                           <td>
                                <?= $rio->comprimento_km ?>
                           </td>
                           <td>
                                <?= $rio->nome_bacia ?>
                           </td>
                    <td>
                        <?= anchor('rio/editar/' . $rio->id_rio, 'Editar') ?> |
                        <?= anchor('rio/deletar/' . $rio->id_rio, 'Deletar') ?>
                    </td>
                </tr>
                <?php endforeach; ?>
                <?php else: ?>
                            <p>Não há registros cadastrados!</p>
                <?php endif ?>
            </tbody>
        </table>
    </body>
</html>

Da mesma forma como fizemos nas bacias, teremos um único arquivo responsável pelo cadastramento e atualização dos dados referentes a esta tabela (rio_new.php):

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?= $titulo ?></title>
    </head>
    <body>
        <h2><?= $cabecalho ?></h2>

        <p><?= validation_errors(); ?></p>

        <?php
        //verifica se o form vai ser usado para insert ou update
        if (!isset($registro->id_rio))
        {
            echo form_open('rio/insere');
        }
        else
        {
            echo form_open('rio/atualizar');

            echo form_hidden('id_rio', $registro->id_rio);
        }
        ?>
        <p>
            <label>Nome do rio:</label>
            <input name="nome_rio" type="text" id="nome_rio" size="40" value="<?= @$registro->nome_rio ?>" />
        </p>

        <p>
            <label>Comprimento (km): </label>
            <input name="comprimento_km" type="text" id="comprimento_km" size="40" value="<?= @$registro->comprimento_km ?>" />
        </p>

        <p>
            <label>Bacia Hidrográfica</label>
            <select name="id_bacia">
                <option value="<?= @$registro->id_bacia; ?>"> <?= @$registro->nome_bacia; ?> </option>
                <?php foreach ($bacias as $bacia): ?>
                <option value="<?= $bacia->id_bacia; ?>"> <?= $bacia->nome_bacia; ?> </option>
                <?php endforeach; ?>
            </select>
        </p>
        <p>
             <input type="submit" id="Enviar" value="Enviar" />
        </p>
               <?= form_close() ?>
    </body>
</html>

Finalizamos o CRUD das duas tabelas, no nosso próximo post, vou mostrar como habilitar o mod_rewrite e retirar da url o index.php, e também fazer algumas pequenas melhorias visuais com CSS. Até lá!

domingo, outubro 10, 2010

Tutorial CodeIgniter - Parte 4

Vamos agora adicionar a validação no formulário de criação das Bacias Hidrográficas e em seguida, no próximo post, finalizar a aplicação com o CRUD dos rios.

A validação no CodeIgniter é feita através da biblioteca  "form_validation", o único problema que eu tive com ela, foi o fato da mesma não aceitar acentos da língua portuguesa,  para resolvermos  isso, vamos extender a classe do core realizando uma modificação no código da função alpha().

Para ver as mensagens dos erros de validação em português, é necessário baixar este arquivo, em seguida descompactar a pasta pt_br em sirh/system/language, feito isso, agora abra o arquivo config.php (dentro da pasta config) e mude a linha 69 para: $config['language'] = "pt-br";

Temos então que copiar o arquivo Form_validation.php da pasta /system/libraries para a pasta /system/application/libraries, agora abra o arquivo copiado e procure pela função alpha(), comente o return dela, e faça a seguinte alteração:

function alpha($str)
{
    //return ( ! preg_match("/^([a-z])+$/i", $str)) ? FALSE : TRUE;
    return (!preg_match("/^([A-Za-zá-úÁ=Ú.\s])+$/i", $str)) ? FALSE : TRUE;
}
 

Modificada a expressão regular, agora é possível trabalhar com palavras acentuadas.

Abra o controller bacia.php, vamos adicionar a validação nas funções insere() e atualizar(). Para isso é necessário Carregar a biblioteca form_validation no método construtor do controlador:

function __construct()
{
    parent::_construct();
    $this->load->model('bacia_model');
    $this->load->library('form_validation');
}



A função insert() ficará assim:

function insere()
{
    // o campo é requerido e deve ser uma string (alpha)
    $this->form_validation->set_rules('nome_bacia', 'Nome da Bacia', 'required|alpha');
    // caso a validação não passe,  carregue a view com o array $data
    if ($this->form_validation->run() == FALSE)
    {
        $data['titulo'] = "SIRH - Nova Bacia Hidrográfica | Erro na Validação, preencha corretamente!";
        $data['cabecalho'] = "Criar Bacia Hidrográfica | Preencha Corretamente!";

        $this->load->view('bacia_new', $data);
    }
    else
    {
        $this->bacia_model->insert();
        $this->session->set_flashdata('msg', 'Registro Criado com Sucesso!');
        redirect('bacia');
    }
}

e a update():

function atualizar()
{
    $id = $this->input->post('id_bacia');

    $this->form_validation->set_rules('nome_bacia', 'Nome da Bacia', 'required|alpha');

    // caso a validação não passe, carregue a view com o array $data
    if ($this->form_validation->run() == FALSE)
    {
        $data['titulo'] = "SIRH - Editar Bacia Hidrográfica | Erro na Validação, preencha corretamente!";
        $data['cabecalho'] = "Editar Bacia Hidrográfica | Preencha Corretamente!";

        $data['registro'] = $this->bacia_model->get_where($id);

        $this->load->view('bacia_new', $data);
    }
    else
    {
        $this->bacia_model->update($id);
        $this->session->set_flashdata('msg', 'Registro Atualizado com Sucesso!');
        redirect('bacia');
    }
} 

A regra adicionada não permite que o campo "nome" da Bacia Hidrográfica fique sem preenchimento (required), e será permitido apenas o preenchimento com texto (alpha). Para sinalizar as mensagens de erro, Abra a view bacia_new.php e adicione a função abaixo, logo depois do cabeçalho:

<p><?= validation_errors(); ?></p>

É isso... no próximo post vou mostrar como ficará o CRUD da tabela rio. Um abraço e até lá!

terça-feira, setembro 28, 2010

Tutorial CodeIgniter - Parte 3

Neste post, vamos criar as funções que irão realizar as operações inserir, atualizar e deletar os registros da tabela bacia_hidrografica.

Antes de começarmos a criar as funções, vamos habilitar a biblioteca "session" do codeigniter e o helper "form", para isso, abra o arquivo autoload.php e modifique as linhas 42 e 54, respectivamente para:

$autoload['libraries'] = array('database', 'session');
$autoload['helper'] = array('url', 'form');

Vamos começar definindo uma função no model que será responsável por inserir os registros na tabela, abra o arquivo bacia_model.php e adicione a seguinte função:

function insert()
{
  $data = $_POST;
  return $this->db->insert('bacia_hidrografica', $data);
}

Já a função abaixo, retorna a linha da tabela com o id_bacia informado, ela será usada para povoar o campo "nome da bacia" do formulário de atualização:

function get_where($id)
{
    $this->db->where('id_bacia', $id);
    $query = $this->db->get('bacia_hidrografica');
    return $query->row();
}

Para realizar o update, usaremos a função abaixo:
function update($id)
{
    $data = $_POST;
    $this->db->where('id_bacia', $id);
    $this->db->update('bacia_hidrografica', $data);
}

Para deletar um registro desta tabela, primeiro faz-se necessário verificar se a mesma não possui nenhum rio vinculado, pois isso vai gerar uma mensagem de erro vinda do banco de dados, em função da constraint foreign key, logo criaremos a função abaixo que fará essa verificação de vínculo:

function verifica_fk($id)
{
    $this->db->where('id_bacia', $id);
    $query = $this->db->get('rio');
    if ($query->num_rows > 0)
    {
        // se houver fk, retorne falso
        return false;
    } else
    {
        return true;
    }
} 

De posse da função acima,  criaremos a função delete():

function delete($id)
{
   // se for verdadeiro, pode apagar o registro
   if ($this->verifica_fk($id))
   {
      $this->db->where('id_bacia', $id);
      return $this->db->delete('bacia_hidrografica');
   }
   else
   {
      return false;
   }
}

Agora, vamos criar duas funções no controller bacia.php, uma para chamar o formulário que será utilizado para a inserção de novos registros, e a outra, que será responsável por efetivar esta operação, através da função definida no model anteriormente (função insert() ):

OBS: Não vamos usar nenhuma função de validação dos dados nesse momento, este será um assunto abordado em um próximo post.

function criar()
{
    $data['titulo'] = "SIRH - Nova Bacia Hidrográfica";
    $data['cabecalho'] = "Criar nova Bacia Hidrográfica";
    $this->load->view('bacia_new', $data);
}

function insere()
{
    $this->bacia_model->insert();
    $this->session->set_flashdata('msg', 'Registro Criado com Sucesso!');
    redirect('bacia');
}

Seguindo o mesmo raciocínio, para realizar a atualização dos registros, vamos criar mais duas funções no controller, editar() e atualizar():

function editar($id)
{
    $data['titulo'] = "SIRH - Editar Bacia Hidrográfica";
    $data['cabecalho'] = "Editar Bacia Hidrográfica";

    $data['registro'] = $this->bacia_model->get_where($id);

    $this->load->view('bacia_new', $data);
}

function atualizar()
{
    $id = $this->input->post('id_bacia');

    $this->bacia_model->update($id);
    $this->session->set_flashdata('msg', 'Registro Atualizado com Sucesso!');
    redirect('bacia');
}

Criamos então a view bacia_new.php, que tanto servirá para inserir, quanto para atualizar, isso é feito através da verificação da existência da variável $registro->id_bacia:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
    <head>
        <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
        <title><?= $titulo ?></title>
    </head>
    <body>
        <h2><?= $cabecalho ?></h2>
        <?php
            //verifica se o form vai ser usado para insert ou update
            if (!isset($registro->id_bacia))
            {
                echo form_open('bacia/insere');
            }
            else
            {
                echo form_open('bacia/atualizar');
                echo form_hidden('id_bacia', $registro->id_bacia);
            }
        ?>
        <p><label>Nome da Bacia:</label>
            <input name="nome_bacia" type="text" id="nome_bacia" size="40" value="<?= @$registro->nome_bacia ?>" />
        </p>

        <p>
            <input type="submit" id="Enviar" value="Enviar" />
        </p>

        <?= form_close() ?>
    </body>
</html>

Finalizando o controller bacia.php, adicionamos a função deletar(), que vai chamar a função delete() do model:

function deletar($id)
{
    if ($this->bacia_model->delete($id))
    {
        $this->session->set_flashdata('msg', 'Registro Deletado com Sucesso!');
    }
    else
    {
        $this->session->set_flashdata('msg', 'ERRO - Não foi possível deletar este registro!<br/>
                                              Primeiro apague os Rios que se relacionam com esta Bacia');
    }
    redirect('bacia');
}

Para terminar, abra o arquivo bacia_view.php e logo abaixo do cabeçalho, chame a função que irá exibir as mensagens para as operações realizadas pelo usuário:

 <p><?=@$this->session->flashdata('msg');?></p>  


Pronto!!! Já temos um CRUD funcional para a tabela bacia_hidrografica, porém sem a validação dos formulários, isso será o assunto da próxima postagem, até lá!

domingo, setembro 26, 2010

Tutorial CodeIgniter - parte 2

Vamos dar continuidade ao desenvolvimento da aplicação usando o CodeIgniter, na pasta sirh/system/application/controllers crie o arquivo bacia.php com o conteúdo abaixo:

<?php
class Bacia extends CI_Controller
{

    function __construct()
    {
        parent::__construct();
    }

    function index()
    {
        echo "<h1>Testando... 1, 2, 3</h1>";
    }
}
 No código anterior, criamos o controlador bacia, com a função index, que é exibida por default quando o controlador é chamado. Logo ao digitarmos a url:  http://localhost/sirh/index.php/bacia, veremos a mensagem de teste (Figura 1).

Figura 1 - teste do controlador bacia
Testado o funcionamento do controller bacia, vamos adicionar o cabeçalho e título da nossa página, modificando o código da função index:

function index()
{
  $data['titulo'] = "SIRH";
  $data['cabecalho'] = "Bacias Hidrográficas";

  $this->load->view('bacia_view', $data);
}

agora, dentro da pasta views, em sirh/system/application/, crie um arquivo com o nome bacia_view.php, com o seguinte conteúdo:


<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
 "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
   <head>
       <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
       <title><?=$titulo?></title>
   </head>

   <body>
      <h2><?=$cabecalho?></h2>
   </body>
</html>

Abra mais uma vez o endereço http://localhost/sirh/index.php/bacia , o resultado deve ser o da Figura 2

Figura 2 - definidos o título e o cabeçalho da página
Está na hora de inserirmos alguns dados para a visualização, execute o script abaixo no banco, para povoar as duas tabelas:

-- INSERINDO 3 Bacias Hidrográficas
INSERT INTO bacia_hidrografica(nome_bacia) VALUES ('Abiaí');
--
INSERT INTO bacia_hidrografica(nome_bacia) VALUES ('Mamanguape');
--
INSERT INTO bacia_hidrografica(nome_bacia) VALUES ('Gramame');

-- INSERINDO 6 Rios nas suas respectivas Bacias
INSERT INTO rio(nome_rio, comprimento_km, id_bacia) VALUES ('Papocas', 9, 1);
INSERT INTO rio(nome_rio, comprimento_km, id_bacia) VALUES ('Graú', 12, 1);
--
INSERT INTO rio(nome_rio, comprimento_km, id_bacia) VALUES ('Mogeiro', 23, 2);
INSERT INTO rio(nome_rio, comprimento_km, id_bacia) VALUES ('Gavião', 9, 2);
--
INSERT INTO rio(nome_rio, comprimento_km, id_bacia) VALUES ('Guarabira',31, 3);
INSERT INTO rio(nome_rio, comprimento_km, id_bacia) VALUES ('Jacaré', 7, 3);

vamos agora exibir os dados que foram inseridos na tabela bacia_hidrografica, para isso será necessário criar um model (dentro de sirh/system/application/model) , que chamaremos de bacia_model.php, com o seguinte conteúdo:

<?php
class Bacia_model extends CI_Model
{
    function  __construct()
    {
        parent:__construct();
    }

    function get_bacia()
    {
        $this->db->order_by('nome_bacia', 'asc');
        $query = $this->db->get('bacia_hidrografica');
        if ($query->num_rows() > 0)
        {
            return $query->result();
        }
        else
        {
            return false;
        }
    }
}

Agora, vamos modificar o nosso controlador, carregando o model criado e povoando uma variável com um array de objetos, obtidos através da função get_bacia():

<?php
class Bacia extends CI_Controller
{

    function __construct()
    {
        parent::__construct();
        $this->load->model('bacia_model');
    }

    function index()
    {
        $data['titulo'] = "SIRH";
        $data['cabecalho'] = "Bacias Hidrográficas";

        $data['bacias'] = $this->bacia_model->get_bacia();

        $this->load->view('bacia_view', $data);
    }
}

Para finalizar esta postagem, vamos agora modificar a nossa view, basicamente vamos varrer o array de objetos $data['bacias'] e exibir os resultados, também vamos utilizar a função anchor() do CodeIgniter para definir os links desta página, que serão finalizados no próximo Post:


<h2><?=$cabecalho ?></h2>

<p><?=anchor('bacia/criar', 'Criar Bacia Hidrográfica') ?></p>

<table border="1">
    <thead>
        <tr>
            <th>
                Nome da Bacia
            </th>
            <th>
                Ações
            </th>
        </tr>
    </thead>
    <tbody>
        <?php if ($bacias != false): ?>
        <?php foreach ($bacias as $bacia): ?>
            <tr>
            <td>
                <?=$bacia->nome_bacia ?>
            </td>
            <td>
                <?=anchor('bacia/editar/' . $bacia->id_bacia, 'Editar') ?> |
                <?=anchor('bacia/deletar/' . $bacia->id_bacia, 'Deletar') ?>
            </td>
        </tr>
        <?php endforeach; ?>
        <?php else: ?>
        <p>Não há registros cadastrados!</p>
        <?php endif ?>
    </tbody>
</table>

Salve todos os arquivos e abra o navegador e veja o resultado: http://localhost/sirh/index.php/bacia

Figura 3 - Exibição das Bacias Hidrográficas
  Bem pessoal, por hoje é só, para ajudar no entendimento deste exemplo, sugiro dar uma boa olhada na série de screencasts do Nettus+ sobre o CodeIgniter (item 5 do link).

Tutorial CodeIgniter - parte 1

O CRUD é definido como acrônimo da expressão inglesa (Create Retrieve Update and Delete), para as quatro operações básicas utilizadas em um bancos de dados relacionais (RDBMS).
Nesta série de  postagens, criaremos uma aplicação Web com CRUD através da linguagem PHP, para as duas tabelas do modelo lógico da Figura 1, utilizando o Framework CodeIgniter. Para codificar, sugiro o uso do Netbeans com o autocomplete do CodeIgniter, este último pode ser baixado no link: http://rhasan.com/blog/2009/09/codeigniter-auto-complete-with-netbeans/ . Já o banco que irei utilizar nesse exemplo, será o PostgreSQL.

OBS: É necessário que o leitor tenha uma base em PHP OO, pois não vou entrar em muitos detalhes, também sugiro, dar uma boa lida no Manual do CodeIgniter em português.


Figura 1 – modelo lógico do exemplo
 
1o Passo: criar o banco no PostgreSQL

Crie um banco com o nome sirh com a codificação UTF-8 e rode o modelo físico abaixo, observe que as chaves primárias foram definidas como serial, criando automaticamente o auto-incremento para esses dois campos:


CREATE TABLE bacia_hidrografica (
id_bacia serial PRIMARY KEY,
nome_bacia varchar(40)
);

CREATE TABLE rio (
id_rio serial PRIMARY KEY,
nome_rio varchar(40),
comprimento_km int,
id_bacia int,
FOREIGN KEY(id_bacia) REFERENCES bacia_hidrografica (id_bacia)
ON UPDATE CASCADE );

Após rodar o script acima, o seu banco ficará da seguinte forma (Figura 2):

Figura 2 - Banco com as duas tabelas criadas (PgAdmin)


2o Passo: download do CodeIgniter

Vá até o endereço: http://codeigniter.com/downloads/ e baixe a última versão do CI (nesse post, usei a versão 1.7.2), a instalação é bem simples, descompacte o arquivo zipado na pasta de publicação do seu servidor web, no meu caso “/var/www/”, para quem usa o MS4W (Mapserver for Windows), descompacte na pasta htdocs do servidor.
Agora, vamos mudar o nome da pasta que foi descompactada para sirh (abreviação de “sistema para recursos hídricos”).

3o Passo: configurar a url do sistema e a conexão com o banco PostgreSQL

Abra  o arquivo config.php, dentro de sirh/system/application/config. Mude a linha 14 para http://localhost/sirh/ , salve o arquivo em seguida.

Abra o arquivo database.php (na mesma pasta) e configure a conexão com o banco (em seguida salve o arquivo):

$db['default']['hostname'] = "localhost";
$db['default']['username'] = "postgres";
$db['default']['password'] = "postgres";
$db['default']['database'] = "sirh";
$db['default']['dbdriver'] = "postgre";

4° Passo: configurar as bibliotecas e os helpers que serão carregados de forma automática

Abra o arquivo autoload.php, modifique a linha 42 para:

 $autoload['libraries'] = array(‘database’); 

e a 54 para:

$autoload['helper'] = array(‘url’);

5° Passo: testar a instalação

Abra o seu navegador e digite a url: http://localhost/sirh, você verá a tela da Figura 3, caso a mesma não seja exibida dessa forma, verifique os passos anteriores. No próximo Post continuaremos a desenvolver esta aplicação, até lá!

figura 3 - tela de boas vindas do codeigniter

quarta-feira, julho 07, 2010

PostGIS: conversão de coordenadas em graus decimais para graus, minutos e segundos (e vice-versa)

Recentemente tive que implementar uma solução, onde através de um formulário, o usuário informava as coordenadas em graus, minutos e segundos, logo em seguida era mostrado em um mapa, se essa coordenada caía em uma área válida da aplicação.
Acontece que por padrão, o PostGIS armazena as geometrias no formato decimal, logo eu teria que tratar as coordenadas digitadas (no código PHP ou no Banco), transformando-as em graus decimais, foi aí que eu encontrei duas funções muito úteis na lista de discussão do PostGIS. Fiz  uma pequena modificação na segunda função, que aumenta a quantidade de casas decimais dos minutos e segundos, caso do resultado seja um número inteiro nesses dois elementos.

Para utilizá-las é necessário carregá-las em um banco com o PostGIS instalado.


Create or Replace Function DMS2DD(  dDeg In Float,
                                    dMin In Float, 
                                    dSec In Float )                                
Returns Float
AS
$BODY$
Declare
   dDD Float;
BEGIN
   dDD := ABS(dDeg) + dMin / 60::float + dSec / 3600::float;
   Return SIGN(dDeg) * dDD;
End;
$BODY$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT
  COST 100;

create or Replace Function DD2DMS(  dDecDeg in Float,
                                    pDegreeSymbol in VarChar(1),
                                    pMinuteSymbol in VarChar(1),
                                    pSecondSymbol in VarChar(1) )
Returns varchar(50)
AS
$BODY$
DECLARE
   iDeg Int;
   iMin Int;
   dSec Float;
   
BEGIN

   iDeg := Trunc(dDecDeg)::Int;
   iMin := Trunc(   (Abs(dDecDeg) - Abs(iDeg)) * 60)::Int;
   dSec := Round(((((Abs(dDecDeg) - Abs(iDeg)) * 60) - iMin) * 60)::numeric, 3)::float;
   
Return trim(to_char(iDeg,'9999')) || pDegreeSymbol::text ||
       trim(to_char(iMin,'99.00')) || pMinuteSymbol::text || 
       case when dSec = 0::Float
       then '00' else replace(trim(to_char(dSec,'99.999')),'.000','') 
       end || pSecondSymbol::text;
END;
$BODY$
  LANGUAGE 'plpgsql' IMMUTABLE STRICT
  COST 100; 

Utilizando as funções:
-- Converter as coordenadas da geometria 
-- POINT(-38.4969 -6.4026) para graus, minutos e segundos: 
 
SELECT DD2DMS(x(geometryFromText('POINT(-38.4969 -6.4026)')), '°','\'','\"' ) as Longitude,
DD2DMS(y(geometryFromText('POINT(-38.4969 -6.4026)')), '°','\'','\"' ) as Latitude;
 

Resultado:

       Longitude      |       Latitude
--------------------------+-----------------------
-38°29.00'48.840" | -6°24.00'9.360"

-- Converter as coordenadas (-34° 10' 50", -7° 33' 30")
-- para graus decimais:
 
SELECT DMS2DD(-34,10,50) as longitude,
DMS2DD(-7,33,30) as latitude;
 

Resultado:

       Longitude       |       Latitude
----------------------------+-----------------------
-34.1805555555556 | -7.55833333333333