José Manuel Gonçalves Alvesintranet.dei.uminho.pt/gdmi/galeria/temas/pdf/30049.pdf · geral - e...
Transcript of José Manuel Gonçalves Alvesintranet.dei.uminho.pt/gdmi/galeria/temas/pdf/30049.pdf · geral - e...
José Manuel Gonçalves Alves
Desenvolvimento de um sistema dinâmicode georeferenciação de conteúdos web
Jos
é M
anue
l Gon
çalve
s Al
ves
Outubro de 2010UMin
ho |
201
0De
senv
olvi
men
to d
e um
sis
tem
a di
nâm
ico
de g
eore
fere
ncia
ção
de c
onte
údos
web
Universidade do MinhoEscola de Engenharia
Outubro de 2010
Dissertação de MestradoCiclo de Estudos Integrados Conducentes aoGrau de Mestre em Engenharia Electrónica Industrial e Computadores
Trabalho efectuado sob a orientação doProfessor Doutor José Mendes
José Manuel Gonçalves Alves
Desenvolvimento de um sistema dinâmicode georeferenciação de conteúdos web
Universidade do MinhoEscola de Engenharia
i
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Agradecimentos
Antes de tudo, queria agradecer à Câmara Municipal de Montalegre (C.M.M.), instituição onde
trabalho, na pessoa do seu presidente, Dr. Fernando Rodrigues, pelo apoio e por me ter criado
as condições para que pudesse realizar este trabalho.
A todos os colegas de trabalho e em particular ao meu colega de gabinete Luís Fidalgo, agradeço
o bom ambiente, apoio e companheirismo que sempre demonstraram.
Ao Doutor José Mendes, que no exercício de orientador, sempre teve uma relação muito aberta
comigo e me proporcionou uma disponibilidade sempre activa para qualquer tipo de
esclarecimento.
Ao meu grupo de amigos, fundamentais no meu dia-a-dia, obrigado pelo apoio que deram
quando precisei.
Por último, mas não menos importante, agradeço a toda a minha família pela educação e
dedicação que devotaram em mim, especialmente à minha mãe, durante estes longos anos de
formação pessoal e académica, são o meu orgulho.
A todos que directamente ou indirectamente participaram no alcance dos meus objectivos
atingidos até aqui os meus sinceros agradecimentos.
ii
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Resumo
O Google Maps foi lançado em Fevereiro de 2005, e ainda em versão beta, tornou-se
rapidamente a referência maior da revolução que a Internet começara então a viver. Com uma
interface rica e intuitiva, a aplicação permitia acesso a uma enorme base de dados contendo
uma infinidade de mapas de cidades, bairros, ruas e avenidas nos Estados Unidos.
Com o tempo, novas localidades e países foram adicionadas ao sistema até que, em meados de
Novembro de 2008, o Google finalmente disponibilizou consultas de endereços em Portugal.
Finalmente, em Dezembro de 2008, foi disponibilizada uma versão completa para o público em
geral - e em português - foi oferecida, com a possibilidade de se localizar restaurantes, hotéis,
traçar rotas, entre outras utilidades.
Assim, cada vez mais sites incluem os mapas do Google no seu conteúdo, com as mais diversas
finalidades: desde a simples localização de estabelecimentos comerciais, até à geração de rotas
de viagem para pontos de interesse.
A inclusão de mapas personalizados em sites de terceiros é possível graças à Google Maps API.
A primeira versão da API (“Application Programming Interface”) do Google Maps foi
disponibilizada em Junho de 2005. Desde então, as funcionalidades foram bastante
aprimoradas, culminando na versão actual, lançada em Dezembro de 2009. A API consiste
basicamente num pequeno conjunto de classes JavaScript que fornecem a interface necessária
para que o programador possa construir aplicações para exibir mapas, realizar consultas por
endereços (geocoding), realizar funções de zoom, acrescentar pontos de referência e descrições
no mapa, entre outras possibilidades. Tudo dentro da "filosofia" AJAX. A API permite gerar o
mapa personalizado e adicionar marcadores através de código JavaScript.
O que se pretende implementar é um sistema de geração de mapas dinâmico baseado em
valores armazenados numa base de dados MySQL. Todas as interfaces (tanto do back-office
como do frontoffice) serão aplicações web, baseadas server-side em PHP, e client-side em
JavaScript (com formatação em HTML e CSS) com filosofia AJAX. Os dados da base de dados
terão que ser transformados dinamicamente em ficheiros XML para parsing pela API do Google
Maps. Esta aplicação permitirá actualizar e criar mapas dinamicamente sem ter que alterar o
código das páginas web onde são incluídos.
Palavras-chave: GOOGLE MAPS API, PHP, HTML, CSS, MYSQL, AJAX, JAVASCRIPT, GIS, CMS
iii
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Abstract
Google Maps, was launched in February 2005 and, still in its beta stage, quickly become a major
reference of the revolution that the Internet began to live then. With a rich and intuitive interface,
the application allowed access to a huge database containing a multitude of maps of cities,
neighborhoods, streets and avenues in the United States.
Over time, new locations and countries were added to the system, until in mid-November 2008,
Google finally released the ability to search Portuguese addresses. In December 2008, a full
version was released to the general public - and in Portuguese - wich offered the ability to locate
restaurants, hotels, map routes, among other uses, now in Portugal.
Thus, more and more websites include Google maps in its content with many different purposes:
from simple location of shops, to the generation of travel routes to points of interest.
Adding custom maps on third party sites is possible thanks to the Google Maps API. The first
version of the API (“Application Programming Interface”) of Google Maps was released in June
2005. Since then, its features were fairly improved, culminating in the current version , released
in December 2009. The API basically consists of a small set of JavaScript classes that provide
the necessary interface for the programmer to build applications to display maps, perform
queries by address (geocoding), perform zoom functions, add waypoints and descriptions on the
map, among other possibilities. Everything within the AJAX philosophy. The API lets you generate
a custom map and add markers with JavaScript code.
The goal of this project is to implement a system for generating dynamic maps based on values
stored in a MySQL database All interfaces (both the back office and the frontoffice) are web
applications based on server-side in PHP and on client-side in JavaScript (with HTML formatting
and CSS) along with the AJAX philosophy. The data from the database will be processed
dynamically in XML files wich will be parsed by the Google Maps API.
This application will allow the update and creation of maps dynamically without the need to
change the code of the web pages on which the maps are included.
Keywords: GOOGLE MAPS API, PHP, HTML, CSS, MYSQL, AJAX, JAVASCRIPT, GIS, CMS
iv
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Índice
INTRODUÇÃO ....................................................................................................... 1
ESTUDO/ANÁLISE DAS TECNOLOGIAS USADAS ..................................................... 3
1. Porquê PHP5, MySQL e AJAX? .................................................................................................... 3
1.1. PHP5 ................................................................................................................................................. 3
1.1.1. Usar PHP em servidores e aplicações Web em modo de produção ................................................. 4
1.1.2. Arquitectura Técnica ...................................................................................................................... 5
1.1.3. Mecanismo Interno ........................................................................................................................ 5
1.1.4. Uma plataforma que resolve problemas de integração .................................................................... 6
1.1.5. O “Ecossistema” PHP .................................................................................................................... 7
1.1.6. Integração num sistema já existente ............................................................................................... 7
1.2. Porquê usar o MySQL? ........................................................................................................................ 7
1.2.1. Visão Geral do MySQL .................................................................................................................... 7
1.2.2. Características técnicas do MySQL ................................................................................................. 8
1.3. AJAX ................................................................................................................................................. 11
1.3.1. Frameworks AJAX ........................................................................................................................ 12
1.3.1.1. Prototype ........................................................................................................................... 12
1.3.1.2. Google WebToolKit ............................................................................................................. 13
1.3.1.3. MooTools ........................................................................................................................... 13
1.3.1.4. Yahoo User Interface .......................................................................................................... 14
1.3.1.5. Adobe Spry ........................................................................................................................ 14
1.3.1.6. jQuery ............................................................................................................................... 15
2. Investigação e experiências com a API do Google Maps ........................................................... 16
2.1. Introdução ........................................................................................................................................ 16
2.2. O Exemplo mais simples do Google Maps ......................................................................................... 17
2.2.1. Carregar a API do Google Maps .................................................................................................... 18
2.2.2. Mapear elementos DOM .............................................................................................................. 18
2.2.3. GMap2 - O objecto elementar e essencial ..................................................................................... 19
2.2.4. Inicializar o mapa ........................................................................................................................ 19
2.2.5. Carregar o mapa ......................................................................................................................... 20
2.3. Latitudes e longitudes ....................................................................................................................... 20
2.4. Atributos do mapa ............................................................................................................................ 22
2.4.1. Tipos de Mapa ............................................................................................................................. 22
2.4.2. Níveis de Zoom ............................................................................................................................ 23
v
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.5. Interacções com o mapa ................................................................................................................... 23
2.6. Janelas de informação ...................................................................................................................... 24
2.7. Eventos ............................................................................................................................................ 25
2.7.1. Visão geral dos eventos do mapa ................................................................................................. 25
2.7.2. Funções que respondem a eventos .............................................................................................. 25
2.7.3. Closures em funções que respondem a eventos ........................................................................... 27
2.7.4. Como usar argumentos passados por eventos .............................................................................. 28
2.7.5. Como vincular eventos a métodos de objectos ............................................................................. 29
2.7.6. Monitorizar eventos DOM ............................................................................................................. 30
2.7.7. Remover escutas de eventos ........................................................................................................ 30
2.8. Controlos .......................................................................................................................................... 31
2.8.1. Visão geral dos controlos ............................................................................................................. 31
2.8.2. Como adicionar controlos ao mapa .............................................................................................. 32
2.8.3. Como posicionar controlos no mapa ............................................................................................ 33
2.9. Sobreposições aos mapas ................................................................................................................. 35
2.9.1. Visão geral das sobreposições ao mapa ....................................................................................... 35
2.9.2. Marcadores ................................................................................................................................. 36
2.9.3. Marcadores arrastáveis ................................................................................................................ 36
2.9.4. Ícones ......................................................................................................................................... 37
2.9.5. Polylines ...................................................................................................................................... 40
2.9.5.1. Como desenhar polylines ................................................................................................... 40
2.9.6. Polígonos ..................................................................................................................................... 41
2.9.7. Sobreposições de KML/GeoRSS .................................................................................................. 42
2.9.8. Rotas ........................................................................................................................................... 43
2.9.8.1. Como carregar rotas .......................................................................................................... 43
2.9.8.2. Modos de transporte .......................................................................................................... 44
2.9.8.3. Manipular as rotas geradas ................................................................................................ 44
DESENVOLVIMENTO DO SISTEMA ...................................................................... 46
3. Modelação de Dados ................................................................................................................. 46
3.1. Modelo ER (entidades e relacionamentos) ......................................................................................... 46
3.2. Descrição das Tabelas da Base de Dados ......................................................................................... 47
3.3. Dicionário de dados .......................................................................................................................... 48
3.4. Particularidades da Modelação de Dados .......................................................................................... 49
4. Mapa Interactivo ....................................................................................................................... 50
4.1. Front-end do mapa ........................................................................................................................... 50
4.1.1. Interface do Mapa (HTML + JavaScript e CSS) .............................................................................. 55
vi
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.1.1. Inicializações Básicas (JavaScript – API) ............................................................................. 57
4.1.1.2. Funções auxiliares JavaScript do front-end ......................................................................... 58
4.1.1.3. Chamadas à camada de dados – XMLHttpRequest() .......................................................... 60
4.1.1.4. Problemas que surgiram durante esta fase ......................................................................... 63
4.1.2. Camadas PHP, MySQL e XML ...................................................................................................... 64
4.1.2.1. Exemplo de output XML gerado .......................................................................................... 65
4.1.2.2. Problemas que surgiram durante esta fase ......................................................................... 66
4.2. Mapa das freguesias: um mapa com particularidades ....................................................................... 66
4.2.1. Acrescentar as delimitações das freguesias ao mapa .................................................................... 67
5. Back-Office ................................................................................................................................ 69
5.1. Gerir Tipos de Localização ................................................................................................................ 69
5.2. Georeferenciação de conteúdo .......................................................................................................... 72
5.3. Utilização da ferramenta CRUD AJAX para gerar o interface do back-office ......................................... 75
CONCLUSÕES .................................................................................................... 78
BIBLIOGRAFIA .................................................................................................... 80
ANEXOS ............................................................................................................. 81
vii
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Índice de ilustrações
Ilustração 1 - Mapa com centro em Montalegre ....................................................................... 17
Ilustração 2 - Marcadores dentro da fronteira do mapa ............................................................ 21
Ilustração 3 - Mapa com imagens de satélite ........................................................................... 22
Ilustração 4 - Janelas de Informação: Info-Windows ................................................................. 25
Ilustração 5 - Escutas de eventos do mapa .............................................................................. 26
Ilustração 6 - Closures ............................................................................................................ 28
Ilustração 7 - Argumentos passados por eventos ..................................................................... 29
Ilustração 8 - Vincular eventos a métodos de objectos ............................................................. 30
Ilustração 9 - Resposta a um clique colocando um marcador no mapa .................................... 31
Ilustração 10 - Mapa com controlos "small" ............................................................................ 33
Ilustração 11 - Marcadores no mapa ....................................................................................... 36
Ilustração 12 - Mapa com ícones ............................................................................................ 38
Ilustração 13 - Ícones Personalizados ...................................................................................... 39
Ilustração 14 - Mapa com polylines ......................................................................................... 40
Ilustração 15 - Mapa com polígonos ........................................................................................ 41
Ilustração 16 - Sobreposição de GeoRSS/KML ........................................................................ 42
Ilustração 17 - Mapa com rota de viagem ................................................................................ 45
Ilustração 18 - Modelo ER da Base de Dados .......................................................................... 46
Ilustração 19 - Camadas do Front-End .................................................................................... 50
Ilustração 20 - Exemplo de mapa gerado ................................................................................. 51
Ilustração 21 - Mapa com Info-Window .................................................................................... 52
Ilustração 22 - Aba para geração da rota ................................................................................. 52
Ilustração 23 - Exemplo de rota ............................................................................................... 52
Ilustração 24 - Listagem descritiva do trajecto ......................................................................... 53
Ilustração 25 - Tabs superiores do mapa ................................................................................. 53
Ilustração 26 - Mapa com o tema "Onde Dormir" .................................................................... 54
Ilustração 27 - Marcador aberto pelo link da lista descritiva ..................................................... 54
Ilustração 28 - Mapa de freguesia com linhas divisórias entre as freguesias e tabs temáticas ... 66
Ilustração 29 - Gerar uma rota até um lugar de uma freguesia ................................................ 67
Ilustração 30 - Ferramenta SHP2KML ..................................................................................... 68
viii
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 31 - Franson CoordTrans ........................................................................................ 68
Ilustração 32 - Grelha CRUD AJAX para gestão dos Tipos de Localização ................................. 70
Ilustração 33 - Formulário in-line para adicionar um novo registo ............................................. 70
Ilustração 34 - Eliminação in-line ............................................................................................. 71
Ilustração 35 - Edição in-line ................................................................................................... 71
Ilustração 36 - Filtros in-line .................................................................................................... 71
Ilustração 37 - Ordenação in-line ............................................................................................. 72
Ilustração 38 - Botão "GEOREFERENCIAR" na etapa final da criação de um conteúdo ............. 72
Ilustração 39 - Formulário de Georeferenciação ....................................................................... 73
Ilustração 40 - Escolha do tipo de localização .......................................................................... 73
Ilustração 41 - Selecção da Localidade .................................................................................... 74
Ilustração 42 - Referenciar um ponto ...................................................................................... 74
Ilustração 43 - Vários pontos associados a um conteúdo ......................................................... 75
Lista de Acrónimos
AJAX – Assincronous JavaScript And XML
API – Application Programming Interface
BD – Base de Dados
CMS – Content Management System (Sistema de Gestão de Conteúdos)
CRUD – Create, Read, Update, Delete (referente a bases de dados)
CSS – Cascade Style Sheets
DB – Database (Base de Dados)
DOM – Document Object Model
GIS – Geographical Information System
HTML – HyperText Markup Language
KML - Keyhole Markup Language
KMZ - Keyhole Markup Language, Zipped
PHP - PHP: Hypertext Preprocessor (acrónimo recursivo)
POI – Point of Interest (Ponto de Interesse)
RSS – Realy Simple Syndication
SGBD – Sistema de Gestão de Bases de Dados
ix
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
SHP – ESRI ShapeFile
SIG – Sistema de Informação Geográfico
SQL – Standard Query Language
XML – Extensible Markup Language
1
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Introdução
Este trabalho teve como génese (além do interesse académico) a necessidade de acrescentar
uma funcionalidade muito importante para qualquer site que promova uma região com grande
volume de turismo.
Quando pesquisamos na web um local de férias, passeio, fim-de-semana, etc. e encontramos
essa informação no site de um destino que nos despertou o interesse, o passo lógico seguinte é
saber como chegar a esse sítio...
Se tivermos toda a informação turística relevante reunida numa só plataforma e ainda por cima
essa plataforma permitir segmentar a informação, tendo ainda, para cada item que nos
interessa, a possibilidade de gerar a rota desde a nossa localização até esse destino, com
certeza terá sucesso.
Esta é a realidade do portal do Município de Montalegre, uma região fortemente turística e com
grande procura de informação de pontos de interesse neste mesmo portal, como podemos ver
na estatística das visitas do último ano:
2
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Depois de toda a evolução que o portal teve desde a primeira versão este módulo faz parte da
evolução natural do Sistema de Gestão de Conteúdos pois constata-se que actualmente todos os
sites de referência incluem plataformas de mapping no seu conteúdo, grande parte deles usando
a plataforma Google Maps.
Os objectivos gerais deste trabalho são:
• Implementar sistema de geração de mapas dinâmico baseado em valores armazenados
numa base de dados MySQL:
o Todas as interfaces serão aplicações web dinâmicas, baseadas server-side em
PHP, e client-side em JavaScript (com formatação em HTML e CSS) com
filosofia AJAX.
o Os dados da base de dados terão que ser transformados dinamicamente em
ficheiros XML para parsing pela API do Google Maps.
o Esta aplicação permitirá actualizar e criar mapas dinamicamente sem ter que
alterar o código das páginas web onde são incluídos
Por envolver a interligação de tantas tecnologias diferentes, este foi um trabalho com um grau de
dificuldade algo elevado, em que o investimento na investigação foi grande e demorado, mas
que compensou largamente aquando da implementação do sistema em si.
3
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Estudo/Análise das tecnologias usadas
1. Porquê PHP5, MySQL e AJAX? 1.1. PHP5
Porque usar PHP5 neste projecto?
Existem no mundo mais de 4.500.000 programadores e utilizadores de PHP.
O PHP é, assim como Java e .NET, uma linguagem de programação e uma plataforma global.
Como linguagem de programação, o PHP tem uma sintaxe bastante parecida com C, Shellscript,
PERL e Java.
Com PHP é possível desenvolver:
• Aplicações Web Dinâmicas (websites, intranets, extranets, etc.)
• Aplicações Desktop (PHP-GTK e PHP4Delphi)
• Rich Clients (PHP-XUL)
• Web Services (SOAP, XML-RPC, REST)
• Scripts de linha de comandos
• Serviços Linux
O PHP é Software Livre e pode ser compilado para várias plataformas.
A portabilidade é uma das maiores vantagens do PHP, possibilitando a sua instalação em vários
Sistemas Operativos como: Linux, Unix, Windows, IBM iSeries, SGI IRIX, RISC OS, Netware
Novell, Mac OS X e AmigaOS
Segundo a NetCraft, o PHP é utilizado em um a cada três sites na Internet. Ou seja, mais de 20
milhões de domínios. 35% da Internet corre PHP.
4
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Uma das maiores plataformas do mundo - o Yahoo - utiliza PHP e chega a receber 150 mil
visitantes simultaneamente.
O Google usa uma linguagem server-side desenvolvida com base no PHP.
1.1.1. Usar PHP em servidores e aplicações Web em modo de produção
Simplicidade
O PHP é uma linguagem com um modelo de desenvolvimento muito simples. O objectivo inicial
do PHP era tornar possível um desenvolvimento rápido. Esse objectivo foi alcançado, fazendo
com que a maioria das empresas de hosting ofereça PHP nos seus servidores.
Adaptabilidade
O PHP usa duas sintaxes. Uma é procedimental e a outra é orientada a objectos. A
procedimental é mais utilizada nos scripts que geram a interface com o utilizador, já a sintaxe
Orientada a Objectos é, propositadamente similar ao Java e C#, com o objectivo de reduzir os
custos com aprendizagem e encorajar a migração para o PHP, sendo tipicamente usada nas
tarefas de processamento e gestão de dados e camada de negócio.
Interoperabilidade
O PHP pode instanciar objectos COM, .NET e JAVA, além de disponibilizar mecanismos para
comunicação com todos os principais sistemas de base de dados relacionais, assim como com
LDAP, XML, Web Services, Lotus Notes, SAP, entre outros.
Portabilidade
Disponível para a maioria dos sistemas operativos, o PHP funciona da mesma maneira que a
Maquina Virtual do Java (JVM). Após desenvolver a aplicação, ela irá funcionar imediatamente,
sem a necessidade de recompilar, independentemente de qual é o sistema operativo utilizado.
Durabilidade
A durabilidade de uma tecnologia depende principalmente da quantidade de utilizadores. O PHP
é utilizado por mais de 4.500.000 programadores no mundo, fazendo com que sua comunidade
seja extremamente forte e actuante, dando fortes garantias do futuro desta plataforma.
Performance
O PHP tem uma grande performance e estabilidade. A combinação Linux/Apache/PHP é
claramente muito forte, como revelam todos os comparativos com outras plataformas
proprietárias.
5
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Retorno do investimento
Os pontos citados acima resultam num rápido retorno do investimento. Isso é possível graças à
ausência de custos com licenças, requisitos de hardware, pouco custo com formação, entre
outros.
1.1.2. Arquitectura Técnica
Na maioria das vezes o PHP é utilizado com:
• Um servidor Web: Apache ou Microsoft IIS
• Um SGDB (Base de Dados): MySQL, PostgreSQL, Oracle, SQL Server, etc.
A maioria das plataformas utiliza Linux, Apache, MySQL e PHP (conhecido como LAMP)
A plataforma pode utilizar as seguintes interfaces:
• Web (HTML, WML, etc.)
• Web Services
• Rich Clients, cliente/servidor (PHP-GTK, PHP-XUL, etc.)
• Linha de Comandos (CLI)
• Documentos Office (Open Documents, Open XML, PDF, Flash, etc.)
A classe FPDF faz com que documentos PDF complexos sejam gerados com poucas linhas de
código.
1.1.3. Mecanismo Interno
O núcleo interno do PHP 5 é o Zend Engine 2. O PHP é feito com uma arquitectura modular, e
utiliza os mesmos conceitos do Java e .NET. Um pré-compilador compila o código e envia-o para
a execução em tempo real.
Esta arquitectura permite que se possam utilizar ferramentas de optimização (opcode cache).
É possível diminuir o tempo de execução de um script em até 66%. Além disso, o PHP oferece
uma API para que seja possível estender suas funcionalidades com módulos adicionais.
Estes módulos permitem uma ligação com bases de dados ou LDAP, executar componentes
Java ou COM, “conversar” com Web Services utilizando SOAP, entre outros.
O PHP pode ser automaticamente actualizado através do PEAR e PECL.
6
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Independente da interface utilizada, a execução segue sempre o mesmo processo. Em primeiro
lugar, o script PHP é interpretado pelo núcleo (core). É nesse ponto que o PHP vai, por exemplo,
ligar à base de dados. Depois disso, o resultado é enviado para o servidor Web ou o servidor GTK
para que seja enviado ao cliente final.
O repositório de classes PEAR oferece mais de 300 componentes adicionais, que são simples de
instalar e actualizar com um único comando.
1.1.4. Uma plataforma que resolve problemas de integração
Muitos conectores
Cerca de 40% dos custos de desenvolvimento estão na integração de aplicações ou fontes de
dados. Para reduzir esses custos, o PHP pode ligar directamente com a maioria das bases de
dados (Oracle, SQL Server, MySQL, dBase, ODBC, XML, etc.), directórios (LDAP, etc.), sistemas
de pagamentos online (Verisign, Cybercash, Realex, etc.) e protocolos (SMTP, IMAP, FTP, HTTP,
TCP, SSH, etc.).
Comunicação via COM, .NET e Web Services
O PHP é o “campeão” no requisito de integração de baixo nível. A plataforma pode facilmente
instanciar objectos COM, classes Java e .NET. PHP 5 com SOAP e tem uma camada de alto
nível para ler ou criar Web Services.
PHP, J2EE e .NET: Utilização concorrente
Graças à possibilidade de trabalhar bem com COM e Java, PHP está-se a tornar num padrão na
gestão da camada gráfica de aplicações que utilizam múltiplas linguagens de programação. Por
exemplo a Lufthansa (Uma das maiores empresas de transporte aéreo do mundo) utiliza PHP
para mostrar a interface visual, e J2EE para a camada de negócio.
Comunicação com .NET
É possível instanciar componentes COM. A classe (ainda experimental) dotnet_load()
permite que um componente Microsoft .NET seja instanciado pelo PHP.
Alguns compiladores do PHP para MSIL (código intermediário do .NET) estão em
desenvolvimento (PHP Sharp)
Há ainda uma implementação open-source para a plataforma .NET: o Mono
Há rumores de que a Oracle irá incluir o PHP no "Oracle Application Server".
, que permite a
instanciação dessas classes sem recorrer a plataformas proprietárias.
O PHP está disponível para as plataformas Netware 6.x.
7
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
1.1.5. O “Ecossistema” PHP
Após 10 anos de vida, o PHP construiu um grande “ecossistema”. Algumas ferramentas tornam
o desenvolvimento mais fácil e rápido.
A comunidade de Software Livre tem vindo a produzir aplicações empresariais há vários anos.
Essas aplicações ajudam empresas a encontrar suas necessidades, proliferando e multiplicando-
se, como num verdadeiro ecossistema.
1.1.6. Integração num sistema já existente
O sistema já em produção onde este trabalho se vai integrar é desenvolvido em PHP. Esta é
porventura a razão que torna o uso do PHP praticamente obrigatório, em detrimento do seu
maior concorrente (que não é open-source): o ASP e ASPX da Microsoft.
1.2. Porquê usar o MySQL?
1.2.1. Visão Geral do MySQL
O MySQL, é o mais popular sistema de gestão de bases de dados SQL open-source, é
desenvolvido, distribuído e tem suporte da MySQL AB – adquirida recentemente pela ORACLE.
O Website da MySQL (http://www.mysql.com/) fornece informações mais recentes sobre e
programa MySQL e a MySQL AB.
O MySQL é um sistema de gestão de bases de dados relacional.
A parte SQL do MySQL é responsável pela “Structured Query Language – Linguagem Estruturada
de Consultas”.
SQL é linguagem padrão mais comum usada para aceder a bases de dados e é definida pelo
padrão ANSI/ISO SQL. (O padrão SQL vem evoluindo desde 1986 e existem diversas versões).
Como é Open Source qualquer pessoa pode fazer download do MySQL pela Internet e usá-lo
sem pagar nada. Se necessário, pode-se estudar o código fonte e alterá-lo para adequá-lo às
necessidades.
8
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
O servidor de base de dados MySQL é extremamente rápido, fiável, e fácil de usar além de ser
grátis.
O Servidor MySQL foi desenvolvido originalmente para lidar com bases de dados muito grandes
de maneira muito mais rápida que as soluções existentes e tem sido usado em ambientes de
produção de alta performance há vários anos de maneira bem sucedida.
Apesar de estar em constante desenvolvimento, o Servidor MySQL oferece hoje um rico e
proveitoso conjunto de funções. A conectividade, velocidade, e segurança fazem com que o
MySQL seja altamente adaptável para aceder bases de dados na Internet.
1.2.2. Características técnicas do MySQL
O Sistema de Base de Dados MySQL é um sistema cliente/servidor que consiste de um servidor
SQL multi-tarefa que suporta acessos diferentes, diversos programas clientes e bibliotecas,
ferramentas administrativas e diversas interfaces de programação (API's).
Muitas aplicações e linguagens já suportam de origem o Servidor de Base de dados MySQL.
Principais Características/Vantagens do MySQL:
• Portabilidade:
o Escrito em C e C++.
o Testado numa ampla faixa de compiladores diferentes.
o Funciona em diversas plataformas.
o Utiliza o GNU Automake, Autoconf, e Libtool para portabilidade.
o APIs para C, C++, Eiffel, Java, Perl, PHP, Python, Ruby e Tcl
o Suporte total a multi-threads usando threads directamente no kernel. Isto significa
que se pode facilmente usar múltiplas CPUs, se disponíveis.
• Fornece mecanismos de armazenamento transaccional e não-transaccional:
o Tabelas em disco (MyISAM) baseadas em árvores-B extremamente rápidas com
compressão de índices.
o É relativamente fácil adicionar outros mecanismos de armazenamento.
o Sistema de alocação de memória muito rápido e baseado em processo(thread).
o Joins muito rápidas usando uma multi-join de leitura única optimizada.
o Tabelas hash em memória que são usadas como tabelas temporárias.
9
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
o Funções SQL são implementadas por meio de uma biblioteca de classes altamente
optimizada e com o máximo de performance. Geralmente não há nenhuma alocação
de memória depois da inicialização da pesquisa.
• Disponível como versão cliente/servidor ou embutida (ligada).
• Tipos de Colunas:
o Aceita diversos tipos de campos: INT de 1, 2, 3, 4 e 8 bytes com e sem sinal,
FLOAT, DOUBLE, CHAR, VARCHAR, TEXT, BLOB, DATE, TIME, DATETIME,
TIMESTAMP, YEAR, SET, ENUM e GEOMETRY.
o GEOMETRY é especificado para dados geo-espaciais.
o Registos de tamanhos fixos ou variáveis.
• Comandos e Funções:
o Completo suporte a operadores e funções nas partes SELECT e WHERE das
consultas.
o Suporte pleno às cláusulas SQL GROUP BY e ORDER BY. Suporte para funções de
agrupamento (COUNT(), COUNT(DISTINCT ...), AVG(), STD(), SUM(), MAX() e MIN()).
o Suporte para LEFT OUTER JOIN e RIGHT OUTER JOIN com as sintaxes SQL e
ODBC.
o Alias em tabelas e colunas são disponíveis como definidos no padrão SQL92.
o DELETE, INSERT, REPLACE, e UPDATE retornam o número de linhas que foram
alteradas (afectadas).
o O comando específico do MySQL SHOW pode ser usado para devolver informações
sobre bases de dados, tabelas e índices. O comando EXPLAIN pode ser usado para
determinar como o optimizador resolve a consulta.
o Nomes de funções não entram em conflito com nomes de tabelas ou colunas. Por
exemplo, ABS é um nome de campo válido.
• Segurança:
o Um sistema de privilégios e palavras-chave que é muito flexível, seguro e que
permite verificação baseada em estações/máquinas. Palavras-chave são seguras
porque todo o tráfego de palavras-chave é criptografado quando na ligação ao
servidor.
10
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
• Escalabilidade e limites:
o Lida com bases de dados enormes. Pode-se usar o Servidor MySQL com bases de
dados que contém 50.000.000 registos. Há conhecimento de utilizadores que usam
o Servidor MySQL com 60.000 tabelas e aproximadamente 5.000.000.000 de
linhas.
o São permitidos até 32 índices por tabela. Cada índice pode ser composto de 1 a 16
colunas ou partes de colunas. O tamanho máximo do índice é de 500 bytes (isto
pode ser alterado na compilação do MySQL). Um índice pode usar o prefixo de
campo com um tipo CHAR ou VARCHAR.
o Permite servidores em Cluster
• Conectividade:
o Os clientes podem-se ligar ao servidor MySQL usando sockets TCP/IP, em qualquer
plataforma. No sistema Windows na família NT (NT, 2000 ou XP), os clientes
podem-se ligar usando named pipes. No sistema Unix, os clientes podem se ligar
usando sockets.
o A interface Connector/ODBC fornece ao MySQL suporte a programas de clientes
que usam conexão ODBC (Open-DataBase-Connectivity). Por exemplo, pode-se usar
o MS Access para ligar ao seu servidor MySQL. Os clientes podem ser executados
no Windows ou Unix.
• IMPORTANTE:
o Contém datatypes específicos para georeferenciação e armazenamento de dados
GIS.
o É o Sistema de Gestão de Bases de Dados usado no CMS que suporta o portal,
sendo por isso essencial para a interligação com as tabelas existentes o uso do
MySQL, ao invés das alternativas também open-source como o postgreSQL ou
comerciais, como Oracle ou Microsoft SQL Server.
11
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
1.3. AJAX
O AJAX (abreviatura de Asynchronous JavaScript and XML) é um conjunto de técnicas de
desenvolvimento web interligadas usada no lado do cliente para criar aplicações web
interactivas.
Com o AJAX, as aplicações web podem carregar dados do servidor de forma assíncrona em
segundo plano sem interferir com a apresentação e comportamento da página existente. A
utilização de técnicas AJAX permite um aumento de interactividade e dinâmicas nas páginas
web.
Os dados assíncronos são geralmente carregados usando o objecto XMLHttpRequest. Apesar do
nome, o uso de XML não é estritamente necessário, nem os pedidos precisam ser sempre
assíncronos.
AJAX não é uma tecnologia em si, mas um grupo de tecnologias associadas. O AJAX usa uma
combinação de HTML e CSS para visualização e formatações de estilos enquanto o DOM é
acedido com JavaScript para exibir dados dinamicamente, permitindo ao utilizador interagir com
as informações apresentadas.
Resumidamente, as funções JavaScript, em conjunto com o objecto XMLHttpRequest fornecem
um método para troca de dados de forma assíncrona entre o browser e o servidor para evitar
reloads de página inteira, permitindo assim actualizar partes ou secções de uma página
independentemente e de forma concorrente.
Constata-se que na Web actual os interfaces dos sites de referência recorrem a esta técnica
aumentando a funcionalidade e interactividade das aplicações, estando a decair rapidamente o
uso da única alternativa: o Adobe Flash, que além de ser um sistema proprietário e fechado, é
muito pouco flexível na interacção com bases de dados e sistemas de terceiros.
É quase obrigatório que todos os interfaces Web desenvolvidos actualmente utilizem interfaces
com o utilizador baseados em AJAX, daí a opção de o usar neste projecto.
12
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
1.3.1. Framew orks AJAX
Com o aparecimento do termo AJAX também começaram a aparecer várias frameworks ou
bibliotecas para desenvolvimento das interfaces das aplicações web. Desde a mais simples em
que apenas ajuda a facilitar o XMLHttpRequest até às mais complicadas em que possibilitam
muito mais do que apenas o XMLHttpRequest.
Fez-se uma análise a algumas das frameworks mais conhecidas e usadas para decidir qual a
mais vantajosa. Todas estas frameworks têm pelo menos uma coisa em comum: todas facilitam
o XMLHttpRequest.
1.3.1.1. Prototype
O Prototype é uma das bibliotecas/framework mais conhecidas e bastante usada para quem
quer usar AJAX. Um dos problemas do Prototype é a actual documentação, que muitos
utilizadores acham complicada e difícil de entender.
• URL: http://www.prototypejs.org/ - Prototype JavaScript framework: Easy AJAX and
DOM manipulation for dynamic web applications – Acedida a 11/08/2010
• Documentação: http://www.prototypejs.org/api
• Sites que usam Prototype: www.digg.com, www.alexa.com
13
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
1.3.1.2. Google WebToolKit
Google oferece o GWT (Google Web Toolkit) que é uma aplicação em JAVA que simplifica a
criação de scripts para usar AJAX. Basicamente permite a qualquer programador criar AJAX
usando JAVA deixando assim de seguida o GWT traduzir o código para JavaScript e HTML.
URL: http://code.google.com/webtoolkit/ - Google Web Toolkit - Acedida a 11/08/2010
• Documentação: http://code.google.com/webtoolkit/overview.html
• Sites que usam GWT: blueprint.lombardi.com, www.dotemplate.com
1.3.1.3. MooTools
A MooTools oferece uma framework para programadores já com alguma experiência em
JavaScript. Ultimamente esta framework tem vindo a crescer e com uma documentação
bastante simples de usar e dá para perceber que a Mootools é uma das frameworks com mais
potencial.
• URL: http://mootools.net - MooTools - a compact JavaScript Framework - Acedida a
11/08/2010
• Documentação: http://docs.mootools.net/
• Sites que usam MooTools: www.cnet.com, joomla.org
14
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
1.3.1.4. Yahoo User Interface
A resposta da Yahoo ao mundo das frameworks. Uma das grandes vantagens de usar a YUI
framework é a possibilidade de não ser preciso fazer o download da framework e poder apenas
criar um link na nossa aplicação directamente aos servidores da Yahoo. Isto pode ser uma
vantagem na redução do tráfego e carga no nosso servidor. Esta Framework, na minha opinião
tem ainda poucos plugins disponíveis e pouca adesão da comunidade de programadores.
• URL: http://developer.yahoo.com/yui/ - YUI Library - Acedida a 12/08/2010
• Documentação: http://developer.yahoo.com/yui/docs/
• Sites que usam YUI: www.linkedin.com, slashdot.org
1.3.1.5. Adobe Spry
Claro que a Adobe não podia fugir deste segmento. Mas como a Adobe já nos habituou, esta
framework está mais inclinada para designers. Para os utilizadores do Dreamweaver, Spry
possibilita uma integração bastante fácil.
• URL: http://labs.adobe.com/technologies/spry/ - Adobe Labs | Spry framework for
AJAX - Acedida a 12/08/2010
• Documentação: http://labs.adobe.com/technologies/spry/
• Sites que usam Spry: www.adobe.com, reader.macrostandard.com
15
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
1.3.1.6. jQuery
A Framework jQuery está nitidamente na frente em relação às outras todas. Tanto ao nível de
documentação, plugins disponíveis, número de utilizadores, uso em sites em produção.
A jQuery oferece mais que o Prototype e em termos de tamanho dos scripts tem muito menos
(16kb) o que é bastante impressionante.
Mais impressionante ainda é o factor de o jQuery ser usado pelo Google Code nalguns sites em
vez da sua própria Framework. (Neste site por exemplo: http://code.google.com/intl/pt-
PT/events/calendar/add - Google Calendar Events – Acedido a 12/08/2010)
• URL: http://jQuery.com - jQuery: The Write Less, Do More, JavaScript Library - Acedido
a 12/08/2010
• Documentação: http://docs.jQuery.com/Main_Page
• Sites que usam jQuery: www.dell.com, www.nbc.com, cbs.com
Depois da análise e experiencias com algumas das frameworks, a escolha recaiu sobre a jQuery.
Quer devido aos plugins já existentes satisfazerem alguns requisitos do projecto, mas também a
pela documentação e a facilidade de utilização.
16
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2. Investigação e experiências com a API do Google Maps
2.1. Introdução A API do Google Maps é o elemento central deste projecto. Esta API permite incluir os mapas do
Google embebidos directamente nas páginas da nossa aplicação, funcionalidade ainda não
disponível nas plataformas de mapping concorrentes como o Bing Maps, Yahoo Maps e Sapo
Mapas.
Antes de começar a desenvolver com esta ferramenta nova, foi estudado o guia/referência para
experimentar as funções e classes fornecidas pela API. A melhor maneira de entender o seu
funcionamento foi mesmo criar pequenas aplicações onde se foram testando as possibilidades
oferecidas pela ferramenta.
17
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.2. O Exemplo mais simples do Google Maps O elemento fundamental de qualquer aplicação da API do Google Maps é o próprio "mapa": o
objecto GMap2. A partir deste objecto desenvolvemos todas as operações sobre o mapa.
A maneira mais fácil de começar com a API do Google Maps v2 é um exemplo simples. O código
seguinte gera uma página web que exibe um mapa 500 x 300 centralizado em Montalegre:
<html xmlns="http://www.w3.org/1999/xhtml"> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"/> <title>Google Maps - Exemplo API JavaScript</title> <script src="http://maps.google.com/maps?file=api&v=2&key=xxxxxxxxxxxxxxxxxxxxxxxxxx&sensor=true_or_false" type="text/JavaScript"></script> <script type="text/JavaScript"> function CMMinitialize() { if (GBrowserIsCompatible()) { var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(41.79179268262892, -7.86895751953125 ), 13); } } </script> </head> <body onload="CMMinitialize()" onunload="GUnload()"> <div id="map_canvas" style="width: 500px; height: 300px"></div> </body>
</html>
Ilustração 1 - Mapa com centro em Montalegre
18
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Para que o Google Maps API funcione no nosso domínio web (e todos os subdomínios
secundários) precisamos de obter uma key, fazendo um processo de registo gratuito junto do
Google.
Neste exemplo simples, há cinco coisas a serem observadas:
1. Incluímos o JavaScript da API do Maps usando uma tag script.
2. Criamos um elemento div chamado "map_canvas" para conter o mapa.
3. Escrevemos uma função JavaScript para criar um objecto "map".
4. Centralizamos o mapa num determinado ponto geográfico.
5. Inicializamos o objecto de mapa a partir do evento onLoad da tag body.
Estas etapas são explicadas a seguir.
2.2.1. Carregar a API do Google Maps
<script src="http://maps.google.com/maps?file=api&v=2&key=xxxxxxxxxxxxxxxxxxxxxxxxxxx&sensor=true_or_false" type="text/JavaScript">
</script>
O URL http://maps.google.com/maps?file=api&v=2&key=xxxxxxxxx aponta para o
local do ficheiro JavaScript que inclui todos os símbolos e as definições necessárias ao uso da
API do Google Maps. A página deve conter uma tag script que aponte para este URL, usando
a chave recebida mediante a inscrição na API. Neste exemplo, a chave é mostrada como
"xxxxxxxxxxx", pois as chaves são únicas para cada cliente.
Também passamos um parâmetro sensor para indicar se esta aplicação usa um sensor para
determinar a localização do utilizador. Deixamos este exemplo como uma variável
true_or_false para enfatizar que se deve definir este valor explicitamente para true ou
false.
2.2.2. Mapear elementos DOM
<div id="map_canvas" style="width: 500px; height: 300px"></div>
Para que o mapa seja mostrado numa página web, precisamos de reservar espaço para ele.
Normalmente, fazemos isto criando um elemento div e criando uma referência a ele no DOM
(document object model ou modelo de objecto do documento) do browser.
19
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
No exemplo acima, definimos uma div com o nome "map_canvas" e configuramos o tamanho
usando atributos de estilo. A menos que especifiquemos explicitamente um tamanho para o
mapa usando GMapOptions no construtor, o mapa usa implicitamente o tamanho do recipiente
para se dimensionar a si mesmo.
2.2.3. GMap2 - O objecto elementar e essencial
var map = new GMap2(document.getElementById("map_canvas"));
Como já referimos, a classe JavaScript que representa um mapa é GMap2. Os objectos desta
classe definem um único mapa numa página. Podemos criar mais do que uma instância desta
classe - cada objecto definirá um mapa separado na página. Criamos uma nova instância desta
classe usando o operador JavaScript new.
Ao criar uma nova instância do mapa, especificamos um nó DOM na página (normalmente um
elemento div) como sendo um recipiente para o mapa. Os nós HTML são filhos do objecto
JavaScript document, e obtemos uma referência a este elemento pelo método
document.getElementById().
Este código define uma variável (com o nome de map) e atribui-a a um novo objecto GMap2. A
função GMap2() é conhecida como construtor e a sua definição (cópia resumida da Referência
da API do Google Maps) é mostrada abaixo:
Construtor Descrição
GMap2(container, opts?)
Cria um novo mapa dentro do container HTML dado, que é normalmente um elemento DIV. Também podemos passar parâmetros opcionais do tipo GMap2Options no parâmetro opts.
Como o JavaScript é uma linguagem com definições de tipos “permissiva”, não precisamos de
passar nenhum parâmetro opcional ao construtor, por isso o não fazemos aqui.
2.2.4. Inicializar o mapa
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
Depois de termos criado um mapa por meio de um construtor GMap2, precisamos de fazer
ainda mais: inicializá-lo. Esta inicialização é realizada por meio do uso do método setCenter()
20
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
do mapa. O método setCenter() exige uma coordenada GLatLng e um nível de zoom, e este
método deve ser enviado antes que quaisquer outras operações sejam executadas no mapa,
incluindo a configuração de quaisquer outros atributos do próprio mapa.
2.2.5. Carregar o mapa
<body onload="initialize()" onunload="GUnload()">
Durante a apresentação de uma página HTML, o DOM é criado, e todas as imagens e scripts
externos são recebidos e incorporados no objecto document. Para garantir que o nosso mapa
seja colocado na página só depois de ela ter sido totalmente carregada, só executamos a função
que cria o objecto GMap2 depois de o elemento <body> da página HTML receber um evento
onload. Isto evita um comportamento imprevisível e dá-nos mais controlo sobre como e quando
o mapa é desenhado.
O atributo onload é um exemplo de um manipulador de evento. A API do Google Maps fornece
ainda uma série de eventos que indicam alterações de estado.
GUnload() é uma função da API projectada para impedir memory leaks, que deve ser sempre usada.
2.3. Latitudes e longitudes
Tendo um mapa, precisamos de uma forma de consultar e incluir os nossos locais/marcadores.
O objecto GLatLng fornece este mecanismo na API do Google Maps. Criamos um objecto
GLatLng, passando os nossos parâmetros na ordem { latitude, longitude } como é normal em
cartografia:
var myGeographicCoordinates = new GLatLng(myLatitude, myLongitude);
Este objecto, tanto é útil na simples consulta a um ponto geográfico, como também na definição
dos limites geográficos de um objecto. Por exemplo, um mapa exibe uma "janela" actual do
mundo inteiro dentro do que é definido como janela de visualização. Esta janela de visualização
pode ser definida por pontos rectangulares nos cantos. O objecto GLatLngBounds oferece esta
funcionalidade, definindo uma região rectangular usando dois objectos GLatLng representando
os cantos inferior esquerdo e superior direito da caixa delimitadora, respectivamente.
21
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Os objectos GLatLng têm muitos usos na API do Google Maps. Por exemplo, o objecto
GMarker utiliza um GLatLng no seu construtor e coloca um marcador em sobreposição ao
mapa, na localização geográfica específica.
Um mapa também contém muitos atributos que vale a pena testar e estudar. Por exemplo, para
encontrar as dimensões da janela de visualização actual, usa-se o método getBounds() do
objecto GMap2 que retorna um valor GLatLngBounds.
O exemplo a seguir usa getBounds() para retornar a janela de visualização actual e coloca,
aleatoriamente, 10 marcadores no mapa dentro destes limites:
function initialize() { var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); // Adiciona 10 marcadores ao mapa em localizações aleatórias var bounds = map.getBounds(); var southWest = bounds.getSouthWest(); var northEast = bounds.getNorthEast(); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for (var i = 0; i < 10; i++) { var point = new GLatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random()); map.addOverlay(new GMarker(point)); }
}
Ilustração 2 - Marcadores dentro da fronteira do mapa
22
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.4. Atributos do mapa 2.4.1. Tipos de Mapa
Por defeito, os mapas aparecem na API do Google Maps usando blocos "pintados" por defeito.
Mas a API do Google Maps também suporta outros tipos de mapa. Os tipos de mapa a seguir
são os disponibilizados por defeito pela API:
• G_NORMAL_MAP – exibe os blocos 2D normais, padrão, do Google Maps • G_SATELLITE_MAP – mostra imagens de satélite/ortofotomapas do Google Earth • G_HYBRID_MAP – mostra uma mistura entre as visualizações normal e de satélite
mostrando em vectorial os recursos importantes (estradas, nomes de cidades, POIs, etc) • G_PHYSICAL_MAP – exibe blocos de mapa físico com base nas informações do terreno • G_DEFAULT_MAP_TYPES – uma matriz destes três tipos, muito útil no processamento
iterativo, ou quando queremos controlar os tipos de mapa com programação (com PHP por exemplo)
Podemos configurar o tipo de mapa usando o método setMapType() do objecto GMap2. Por
exemplo, o código a seguir define que o mapa deve usar a visualização de satélite do Google
Earth:
var map = new GMap2(document.getElementById("map_canvas")); map.setMapType(G_SATELLITE_MAP);
Também é possível definir tipos de mapas personalizados caso haja imagens ou sobreposições
definidas. O código abaixo remove G_HYBRID_MAP dos tipos disponíveis anexados a um mapa,
deixando apenas dois tipos de mapa. Depois de adicionarmos GMapTypeControl, apenas
estes dois tipos de mapa permanecem disponíveis:
var map = new GMap2(document.getElementById("map_canvas"), { size: new GSize(640,320) } ); map.removeMapType(G_HYBRID_MAP); map.setCenter(new GLatLng(42.366662,-71.106262), 11); var mapControl = new GMapTypeControl(); map.addControl(mapControl); map.addControl(new GLargeMapControl());
Ilustração 3 - Mapa com imagens de satélite
23
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.4.2. Níveis de Zoom
Cada mapa também contém um nível de zoom, que define a resolução da visualização actual.
Níveis de zoom entre 0 (o mais baixo, no qual todo o mundo pode ser visto num só mapa) e 19
(o mais alto, que chega a construções individuais) são possíveis na visualização normal dos
mapas. Os níveis de zoom variam de acordo com o sítio do mundo que estamos a ver, porque os
dados nalgumas partes do mundo estão mais definidos do que em outras. Os níveis de zoom até
19 só são possíveis na visualização de satélite nalguns sítios. Em Portugal há ainda muitos sítios
onde isso não é possível e a zona de Montalegre é uma delas, permitindo nível de zoom máximo
16.
Podemos consultar o nível de zoom actual utilizado pelo mapa usando o método getZoom() do
objecto GMap2.
2.5. Interacções com o mapa Agora que temos um objecto GMap2, podemos interagir com ele. O objecto de mapa básico é
semelhante e comporta-se de maneira muito parecida com o mapa de interacção do site do
Google Maps, além de vir com uma grande quantidade de comportamentos integrados. O
objecto GMap2 também fornece vários métodos de configuração para alterar o comportamento
do próprio objecto de mapa.
Por defeito, os objectos do mapa tendem a reagir à actividade do utilizador como acontece em
http://maps.google.pt. Podemos alterar este comportamento usando vários métodos da API. Por
exemplo, o método GMap2.disableDragging() desactiva a capacidade de clicar e arrastar o
mapa para um novo local.
Também podemos interagir com o mapa via programação. O objecto GMap2 suporta vários
métodos que alteram o estado do mapa directamente. Por exemplo, os métodos setCenter(),
panTo() e zoomIn() operam no mapa de maneira programável, e não pela interacção do
utilizador.
O método panTo centraliza o mapa num determinado ponto. Caso o ponto especificado esteja
na parte visível do mapa, o mapa desloca-se lentamente até este ponto; caso contrário, o mapa
"salta" para o ponto:
24
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); window.setTimeout(function() { map.panTo(new GLatLng(37.4569, -122.1569)); }, 1000);
2.6. Janelas de informação Cada mapa da API do Google Maps pode mostrar "janelas de informações" do tipo
GInfoWindow, que exibem conteúdo HTML numa janela flutuante acima do mapa. A janela de
informações parece-se um pouco com os balões das histórias de banda desenhada. Esta
apresenta uma área de conteúdo e uma base ancorada, cuja ponta permanece num ponto
específico do mapa.
O objecto GInfoWindow não possui nenhum construtor. Uma janela de informações é
automaticamente criada e anexada ao mapa quando o criamos. Não é possível mostrar mais de
uma janela de informações de cada vez num determinado mapa, mas podemos movê-la e
alterar o seu conteúdo quando necessário.
Quando abrimos uma nova janela, a que estava aberta anteriormente fecha-se automaticamente.
O objecto GMap2 fornece o método openInfoWindow() que usa um ponto e um elemento
DOM HTML como argumentos. O elemento DOM HTML é anexo ao recipiente da janela de
informações e a extremidade da janela é ancorada ao ponto específico.
O método openInfoWindowHtml() do GMap2 é semelhante, mas usa uma string HTML como
segundo argumento, em vez de um elemento DOM, para apresentar conteúdo formatado em
HTML.
Para criar a janela de informações, chama-se o método openInfoWindow(), passando um
local e um elemento DOM a serem mostrados. O código de exemplo a seguir exibe uma janela
de informações ancorada ao centro do mapa com a simples mensagem "Hello, world":
var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); map.openInfoWindow(map.getCenter(),
document.createTextNode("Hello, world"));
25
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 4 - Janelas de Informação: Info-Windows
2.7. Eventos 2.7.1. Visão geral dos eventos do mapa
O JavaScript no browser é orientado a eventos, o que significa que o JavaScript responde a
interacções gerando eventos e espera que um programa/script escute os eventos que lhe
“interessam”. Por exemplo, nos browsers, interacções de rato e teclado do utilizador criam
eventos propagados no DOM. Os programas interessados em determinados eventos registam
escutas de eventos JavaScript para estes eventos e executarão o código quando os eventos
forem recebidos.
A API do Google Maps faz inclusões neste modelo de eventos, definindo eventos personalizados
para objectos da API do Google Maps. É importante observar que os eventos da API do Google
Maps são separados e distintos dos eventos DOM padrão. No entanto, como browsers diferentes
implementam modelos de evento DOM diferentes, a API do Google Maps também fornece
mecanismos para ouvir e responder a estes eventos DOM sem a necessidade de manipular as
muitas peculiaridades dos vários browsers.
2.7.2. Funções que respondem a eventos
Os eventos na API do Google Maps são manipulados com funções dentro do namespace
GEvent para registar funções que respondem a eventos. Cada objecto da API do Maps exporta
vários named events. Por exemplo, o objecto GMap2 exporta os eventos click, dblclick e
move, além de muitos outros. Cada evento acontece num determinado contexto, podendo
passar argumentos que identificam este contexto. Por exemplo, o evento mousemove é activado
quando o utilizador move o rato num objecto de mapa e passa a GLatLng da localização
geográfica na qual se encontra o rato.
26
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
A lista completa dos eventos GMap2 e dos argumentos gerados está na Referência da API do
Google Maps.
Para se inscrever para receber notificações destes eventos, usa-se o método estático
GEvent.addListener(). Este método usa um objecto, um evento a ser ouvido e uma função
a ser chamada quando ocorre o evento especificado. Por exemplo, este excerto de código mostra
um alerta sempre que o utilizador clica no mapa:
var map = new GMap2(document.getElementById("map")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); GEvent.addListener(map, "click", function() { alert("Clicou no mapa.");
});
Ilustração 5 - Escutas de eventos do mapa
As funções que respondem a eventos também podem capturar o contexto do evento. No
exemplo de código abaixo, é mostrada a latitude e a longitude do centro do mapa depois de o
utilizador arrastar o mapa:
var map = new GMap2(document.getElementById("map")); GEvent.addListener(map, "moveend", function() { var center = map.getCenter(); document.getElementById("message").innerHTML = center.toString(); });
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
27
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.7.3. Closures em funções que respondem a eventos
Durante a execução de uma função que responde a um evento, geralmente é vantajoso ter
dados privados e persistentes anexados a um objecto. O JavaScript não oferece suporte a dados
de instância "privada", mas oferece closures1, os quais permitem que funções internas acedam
a variáveis externas. Os closures são úteis em funções que respondem a eventos para aceder a
variáveis que não são geralmente anexas aos objectos nos quais ocorrem os eventos.
O exemplo a seguir usa um closure de função na função que responde a um evento para atribuir
uma mensagem secreta a um conjunto de marcadores. Clicar em cada um dos marcadores
apresentará um excerto da mensagem secreta, que não está contida no marcador propriamente
dito:
var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); // Cria marcador num determinado ponto // Os cinco marcadores mostram uma mensagem secreta // mas a mensagem não está nos dados da instância do marcador function createMarker(point, number) { var marker = new GMarker(point); var message = ["Esta","é","a","mensagem","secreta"]; marker.value = number; GEvent.addListener(marker, "click", function() { var myHtml = "<b>#" + number + "</b><br/>" + message[number -1]; map.openInfoWindowHtml(point, myHtml); }); return marker; } // Adicionar 5 marcadores ao mapa em localizações aleatórias var bounds = map.getBounds(); var southWest = bounds.getSouthWest(); var northEast = bounds.getNorthEast(); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for (var i = 0; i < 5; i++) { var point = new GLatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random()); map.addOverlay(createMarker(point, i + 1));
}
1 http://jibbering.com/faq/notes/closures/
28
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 6 - Closures
2.7.4. Como usar argumentos passados por eventos
Quando accionados, muitos eventos no sistema da API do Google Maps passam argumentos.
Por exemplo, o evento de "clique" GMap2 passa argumentos overlay e overlaylatlng se o
clique do mapa ocorrer numa sobreposição; caso contrário, ele passa um latlng da
coordenada do mapa. Pode-se aceder a estes argumentos passando os símbolos especificados
directamente para as funções nas escutas de evento.
No exemplo a seguir, primeiro há um teste para garantir que o clique foi feito sobre um bloco do
mapa verificando se o argumento latlng está definido. Se estiver, abre-se uma janela de
informações acima da coordenada clicada e exibe-se a coordenada convertida em espaço de
pixel juntamente com o nível de zoom:
var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); GEvent.addListener(map,"click", function(overlay, latlng) { if (latlng) { var myHtml = "O valor GPoint é: " + map.fromLatLngToDivPixel(latlng) + " com zoom nível " + map.getZoom(); map.openInfoWindow(latlng, myHtml); } }); map.addControl(new GSmallMapControl()); map.addControl(new GMapTypeControl());
29
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 7 - Argumentos passados por eventos
2.7.5. Como vincular eventos a métodos de objectos
As funções são úteis quando se deseja anexar escutas de evento a instâncias específicas de um
objecto. No entanto, se quisermos chamar um método em todas as instâncias de um objecto em
resposta a eventos, podemos usar GEvent.bind(). No exemplo a seguir, uma instância da
classe de aplicação vincula eventos de mapa aos seus próprios métodos, modificando o estado
da classe quando os eventos são disparados:
function MyApplication() { this.counter = 0; this.map = new GMap2(document.getElementById("map")); this.map.setCenter(new GLatLng(37.4419, -122.1419), 13); GEvent.bind(this.map, "click", this, this.onMapClick); } MyApplication.prototype.onMapClick = function() { this.contador++; alert("Clicou no mapa " + this.contador + " " + (this.counter == 1 ? "vez" : "vezes")); }
var application = new MyApplication();
30
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 8 - Vincular eventos a métodos de objectos
2.7.6. Monitorizar eventos DOM
O modelo de eventos da API do Google Maps cria e gere eventos personalizados próprios.
Porém, o DOM também cria e distribui os seus próprios eventos, de acordo com o modelo de
eventos usado pelo browser em especial. Caso se queira capturar e responder a estes eventos, a
API do Google Maps fornece mecanismos para ouvir e vincular eventos DOM sem que haja a
necessidade de código personalizado.
O método estático GEvent.addDomListener() regista um manipulador de evento para um
evento e um nó DOM. Da mesma forma, o método estático GEvent.bindDom() permite
registar manipuladores de evento para eventos DOM em instâncias de classe.
2.7.7. Remover escutas de eventos
Quando uma escuta de evento deixa de ser necessária deve remover-se. Isto pode ser necessário
até mesmo em casos nos quais o evento deve ser accionado apenas uma vez. Remover escutas
de evento definidas por meio de funções anónimas dentro dos closures pode ser difícil. No
entanto, as funções addListener(), addDomListener(), bind() e bindDom() retornam
um manipulador GEventListener, que pode ser usado para, no final, cancelar o registo deste
manipulador.
O exemplo a seguir responde a um clique colocando um marcador no mapa. Qualquer clique
subsequente remove a escuta de evento. Em consequência disto, o código removeOverlay()
nunca é executado. Além disto, pode-se remover uma escuta de evento da própria escuta de
evento:
31
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
function MyApplication() { this.counter = 0; this.map = new GMap2(document.getElementById("map")); this.map.setCenter(new GLatLng(37.4419, -122.1419), 13); var myEventListener = GEvent.bind(this.map, "click", this, function(overlay, latlng) { if (this.counter == 0) { if (latlng) { this.map.addOverlay(new GMarker(latlng)) this.counter++; } else if (overlay instanceof GMarker) { this.removeOverlay(marker) } } else { GEvent.removeListener(myEventListener); } }); } function load() { var application = new MyApplication(); }
Ilustração 9 - Resposta a um clique colocando um marcador no mapa
2.8. Controlos 2.8.1. Visão geral dos controlos
Os mapas no site http://maps.google.pt contêm elementos de interface do utilizador que
possibilitam sua interacção com todo o mapa. Estes elementos são conhecidos como controlos,
e podemos incluir variações destes controlos na aplicação da API do Google Maps. Também é
possível criar controlos personalizados próprios criando subclasses na classe GControl.
A API do Google Maps contém vários controlos incorporados que podemos usar nos mapas:
• GLargeMapControl - um controlo de panorâmica/zoom grande usado no Google
Maps. Por defeito, aparece no canto superior esquerdo do mapa.
32
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
• GSmallMapControl - um controlo de panorâmica/zoom pequeno usado no Google
Maps. Por defeito, aparece no canto superior esquerdo do mapa.
• GSmallZoomControl - um controlo de zoom pequeno (sem controlos de panorâmica)
usado nas janelas de ampliação do mapa que costumam mostrar instruções de rota no
Google Maps.
• GScaleControl – controlo da escala do mapa
• GMapTypeControl - botões que permitem ao utilizador alternar tipos de mapa (como,
por exemplo, Mapa e Satélite)
• GHierarchicalMapTypeControl - selector de com botões “hierarquizados” com
todos os tipos de mapa.
• GOverviewMapControl - um mapa tipo janela de “overview” que pode ser escondido
no canto da tela.
Todos estes controlos se baseiam no objecto GControl.
GMapTypeControl e GHierarchicalMapTypeControl são casos especiais porque
também podem ser configurados. Estes controlos adicionam a funcionalidade de alterar o
GMapType usado em cada momento pelo mapa na API do Google Maps.
2.8.2. Como adicionar controlos ao mapa
É possível adicionar controlos ao mapa usando o método addControl() da classe GMap2. Por
exemplo, para adicionar o controlo de panorâmica/zoom visto no site do Google Maps ao mapa,
devemos incluir esta linha na inicialização do mapa:
map.addControl(new GLargeMapControl());
Podemos adicionar vários controlos a um mapa. Neste caso, adicionamos os controlos
integrados GSmallMapControl e GMapTypeControl, que permitem obter uma visualização
panorâmica ou aplicar zoom ao mapa e alternar entre os modos de Mapa, Satélite e Híbrido
respectivamente. Uma vez incluídos num mapa, os controlos padrão ficam logo totalmente
operacionais:
33
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var map = new GMap2(document.getElementById("map"));
map.addControl(new GSmallMapControl());
map.addControl(new GMapTypeControl());
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
Ilustração 10 - Mapa com controlos "small"
2.8.3. Como posicionar controlos no mapa
O método addControl usa um parâmetro GControlPosition secundário opcional que
permite especificar a posição do controlo no mapa. Este valor pode ser um dos valores a seguir,
cada um especificando um canto do mapa no qual o controlo é inserido:
• G_ANCHOR_TOP_RIGHT
• G_ANCHOR_TOP_LEFT
• G_ANCHOR_BOTTOM_RIGHT
• G_ANCHOR_BOTTOM_LEFT
Caso o argumento seja excluído, a API do Google Maps usa a posição padrão especificada pelo
controlo que é G_ANCHOR_TOP_LEFT .
O Método PGControlPosition dá-nos a hipótese de especificar um deslocamento indicando
a quantos pixels da borda do mapa o controlo deve ser colocado. Estes deslocamentos são
especificados com um objecto GSize.
Este exemplo adiciona o GMapTypeControl ao canto superior direito do mapa com 10 pixels
de preenchimento.
Clicar duas vezes em qualquer sítio do mapa remove este controlo e coloca-o no canto inferior
direito do mapa:
34
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var map = new GMap2(document.getElementById"map_canvas"));
var mapTypeControl = new GMapTypeControl();
var topRight = new GControlPosition(G_ANCHOR_TOP_RIGHT, new
GSize(10,10));
var bottomRight = new GControlPosition(G_ANCHOR_BOTTOM_RIGHT, new
GSize(10,10));
map.addControl(mapTypeControl, topRight);
GEvent.addListener(map, "dblclick", function() {
map.removeControl(mapTypeControl);
map.addControl(new GMapTypeControl(), bottomRight);
});
map.addControl(new GSmallMapControl());
map.setCenter(new GLatLng(37.4419, -122.1419), 13);
35
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9. Sobreposições aos mapas 2.9.1. Visão geral das sobreposições ao mapa
As sobreposições são objectos incluídos no mapa e relacionados com ele através coordenadas
de latitude/longitude que se movem quando se aplica zoom ao mapa ou se arrasta. Elas
reflectem objectos "adicionados" ao mapa para designar pontos, linhas ou áreas.
A API do Google Maps conta com vários tipos de sobreposição:
• Os pontos no mapa são exibidos usando marcadores e costumam indicar um ponto
através de um ícone personalizado. Marcadores são objectos do tipo GMarker e podem
fazer uso do tipo GIcon.
• As linhas no mapa são exibidas usando polylines (que representam uma série de
pontos). As linhas são objectos do tipo GPolyline.
• As áreas no mapa são exibidas como polígonos, se forem áreas de formato aleatório,
ou como sobreposições de solo, se forem rectangulares. Os polígonos são
semelhantes às polylines por consistirem numa série de pontos que formam uma figura
fechada e poderem assumir qualquer forma. As sobreposições de solo costumam ser
usadas em áreas que fazem mapeamento directo ou indirecto para blocos do mapa.
• O próprio mapa é exibido usando uma sobreposição de blocos. Podemos modificar
isto com o seu próprio conjunto de blocos usando uma GTileLayerOverlay ou
criando seu tipo de mapa usando um GMapType.
• A janela de informações também é um tipo especial de sobreposição. No entanto, a
janela de informações é adicionada automaticamente ao mapa e só pode haver um
objecto do tipo GInfoWindow anexado ao mapa.
Cada sobreposição implementa a interface GOverlay. As sobreposições podem ser adicionadas
a um mapa usando o método GMap2.addOverlay() e removidas usando o método
GMap2.removeOverlay(). Por defeito, a janela de informações é adicionada ao mapa sem
chamar estes métodos.
36
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9.2. Marcadores
Os marcadores identificam pontos no mapa. Por defeito, eles usam G_DEFAULT_ICON, muito
embora se possa especificar um ícone personalizado. O construtor GMarker usa um GLatLng e
objectos GMarkerOptions opcionais como argumentos.
Os marcadores foram projectados para serem interactivos. Por defeito, eles recebem eventos
"click", por exemplo, e costumam ser usados em escutas de evento para exibir janelas de
informações:
var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); // Adicionar 10 marcadores ao mapa em localizações aleatórias var bounds = map.getBounds(); var southWest = bounds.getSouthWest(); var northEast = bounds.getNorthEast(); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for (var i = 0; i < 10; i++) { var point = new GLatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random()); map.addOverlay(new GMarker(point)); }
Ilustração 11 - Marcadores no mapa
2.9.3. Marcadores arrastáveis
Os marcadores podem ser objectos interactivos ser clicados e arrastados para um novo local.
Neste exemplo, colocamos um marcador arrastável no mapa e escutamos alguns de seus
eventos mais simples. Marcadores arrastáveis implementam quatro tipos de eventos: click,
dragstart, drag e dragend, para indicar o status do arrastamento. Por defeito, os
marcadores são clicáveis, mas não arrastáveis e, por isto, precisam de ser inicializados com a
opção de marcador adicional draggable definida como "true". Por defeito, os marcadores
37
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
arrastáveis também têm a capacidade de saltar. Caso não gostemos deste comportamento,
basta definir a opção bouncy como "false", e ele permanecerá suspenso:
var map = new GMap2(document.getElementById("map_canvas")); var center = new GLatLng(37.4419, -122.1419); map.setCenter(center, 13); var marker = new GMarker(center, {draggable: true}); GEvent.addListener(marker, "dragstart", function() { map.closeInfoWindow(); }); GEvent.addListener(marker, "dragend", function() { marker.openInfoWindowHtml("A arrastar no mapa..."); }); map.addOverlay(marker);
2.9.4. Ícones
Os marcadores podem ter um ícone personalizado em vez do ícone padrão. Definir um ícone
personalizado é algo complexo por causa do número de imagens diferentes que constituem um
objecto Icon da API do Google Maps. Um ícone deve, no mínimo, definir a imagem de primeiro
plano, o tamanho do tipo GSize e uma imagem/sombra de deslocamento para posicionamento
do ícone.
Os ícones mais simples baseiam-se no tipo G_DEFAULT_ICON. Criar um ícone com base neste
tipo permite alterar rapidamente o ícone padrão modificando apenas algumas propriedades.
No exemplo abaixo, criamos um ícone usando o tipo G_DEFAULT_ICON e modificamo-lo para
usar uma imagem diferente. É preciso ter cuidado ao usar imagens diferentes, porque elas
precisam de ser configuradas de acordo com o tamanho da imagem padrão do GoogleMaps
para que sejam exibidas correctamente:
var map = new GMap2(document.getElementById("map_canvas")); map.addControl(new GSmallMapControl()); map.setCenter(new GLatLng(37.4419, -122.1419), 13); // Criar o nosso "mini" icone de marcador var blueIcon = new GIcon(G_DEFAULT_ICON); blueIcon.image = "http://localhost/gmaps/micons/blue-dot.png"; // Definir as opções do objecto GMarkerOptions markerOptions = { icon:blueIcon }; // Adicionar 10 marcadores ao mapa em localizações aleatórias var bounds = map.getBounds(); var southWest = bounds.getSouthWest(); var northEast = bounds.getNorthEast();
38
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for (var i = 0; i < 10; i++) { var point = new GLatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random()); map.addOverlay(new GMarker(point, markerOptions)); }
Ilustração 12 - Mapa com ícones
A maioria dos ícones contém uma imagem em primeiro plano e uma imagem sombreada. A
imagem sombreada deve ser criada num ângulo de 45 graus, em relação à imagem em primeiro
plano, e o canto inferior esquerdo da imagem sombreada deve permanecer alinhado com o
canto inferior esquerdo da imagem em primeiro plano do ícone. Para que os limites da imagem
sejam exibidos correctamente no mapa, as imagens sombreadas devem ser PNG de 24 bits com
transparência alfa. (Que vai ser um problema em versões do Internet Explorer inferior à 8, tendo
que usar a solução do DirectXAlfa para essas versões).
O exemplo seguinte cria um novo tipo de ícone, usando "mini marcadores" personalizados.
Precisamos especificar a imagem de primeiro plano, a imagem sombreada e os pontos nos
quais ancoramos o ícone ao mapa e a janela de informações ao ícone. O ícone é passado nas
opções definidas no objecto GMarkerOptions:
var map = new GMap2(document.getElementById("map")); map.addControl(new GSmallMapControl()); map.addControl(new GMapTypeControl()); map.setCenter(new GLatLng(37.4419, -122.1419), 13); // Criar o nosso "mini" icone de marcador e sombra var tinyIcon = new GIcon(); tinyIcon.image = "http://localhost/gmaps/micons/mm_20_castanho.png"; tinyIcon.shadow = "http://localhost/gmaps/micons/mm_20_sombra.png"; tinyIcon.iconSize = new GSize(12, 20); tinyIcon.shadowSize = new GSize(22, 20);
39
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
tinyIcon.iconAnchor = new GPoint(6, 20); tinyIcon.infoWindowAnchor = new GPoint(5, 1); // Definir as opções do objecto GMarkerOptions markerOptions = { icon:tinyIcon }; // Adicionar 10 marcadores ao mapa em localizações aleatórias var bounds = map.getBounds(); var southWest = bounds.getSouthWest(); var northEast = bounds.getNorthEast(); var lngSpan = northEast.lng() - southWest.lng(); var latSpan = northEast.lat() - southWest.lat(); for (var i = 0; i < 10; i++) { var point = new GLatLng(southWest.lat() + latSpan * Math.random(), southWest.lng() + lngSpan * Math.random()); map.addOverlay(new GMarker(point, markerOptions)); }
Ilustração 13 - Ícones Personalizados
40
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9.5. Polylines
Os objectos GPolyline criam uma sobreposição linear no mapa. GPolyline consiste numa
série de pontos e cria uma série de segmentos de recta que ligam estes pontos numa sequência
ordenada.
2.9.5.1. Como desenhar polylines
As polylines são desenhadas como uma série de segmentos de recta no mapa. Podemos
especificar cores personalizadas, densidades e opacidades para a linha. As cores devem usar o
código HTML numérico hexadecimal, por exemplo, usar #ff0000 em vez de red. GPolyline
não compreende cores com nomes.
Os objectos GPolyline usam os recursos de desenho vectorial do browser, em caso de
disponibilidade. No Internet Explorer, o Google Maps usa VML para desenhar polylines. Noutros
browsers, usa-se SVG, se disponível.
O fragmento de código a seguir cria uma polyline vermelha com 10 pixels de largura entre dois
pontos:
var polyline = new GPolyline([ new GLatLng(37.4419, -122.1419), new GLatLng(37.4519, -122.1519) ], "#ff0000", 10); map.addOverlay(polyline);
Ilustração 14 - Mapa com polylines
41
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9.6. Polígonos
Os objectos GPolygon são semelhantes a objectos GPolyline, no sentido em que eles
consistem numa série de pontos numa sequência ordenada. No entanto, em vez de terem final
aberto, os polígonos foram projectados para definir regiões numa figura fechada. Assim como
acontece com as polylines, podemos definir cores, densidades e opacidades personalizadas para
a borda do polígono (a "linha"), além de cores e opacidades personalizadas para a área de
preenchimento da região abrangida. As cores devem estar em código HTML numérico
hexadecimal.
Os objectos GPolygon, assim como os objectos GPolyline, usam os recursos de desenho
vectorial do browser, quando disponível.
O fragmento de código seguinte cria uma caixa com 10 pixéis de largura usando quatro pontos.
Este polígono é "fechado" ao retornar o caminho do segmento ao seu ponto inicial:
var map = new GMap2(document.getElementById("map_canvas")); map.setCenter(new GLatLng(37.4419, -122.1419), 13); map.addControl(new GSmallMapControl()); GEvent.addListener(map, 'click', function(overlay, latlng) { var lat = latlng.lat(); var lon = latlng.lng(); var latOffset = 0.01; var lonOffset = 0.01; var polygon = new GPolygon([ new GLatLng(lat, lon - lonOffset), new GLatLng(lat + latOffset, lon), new GLatLng(lat, lon + lonOffset), new GLatLng(lat - latOffset, lon), new GLatLng(lat, lon - lonOffset) ], "#f33f00", 5, 1, "#ff0000", 0.2); map.addOverlay(polygon); });
Ilustração 15 - Mapa com polígonos
42
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9.7. Sobreposições de KML/GeoRSS
A API do Google Maps oferece suporte aos formatos de dados KML (criados no Google Earth) e
GeoRSS para exibir informações geográficas. Estes formatos de dados são adicionados ao mapa
usando o objecto GGeoXml, cujo construtor usa o URL de um ficheiro XML acessível
publicamente. Os marcadores GGeoXml são apresentados como GMarkers, ao passo que
polylines e polígonos GGeoXml são renderizados como polylines e polígonos da API do Google
Maps.
Os objectos GGeoXml são adicionados ao mapa pelo método addOverlay(). Podemos
removê-los do mapa usando removeOverlay().
Há suporte para ficheiros KML e XML GeoRSS. GGeoXml é um objecto modular da API do
Google Maps, que não será totalmente carregado até ser usado pela primeira vez. Desta forma,
só se invoca o construtor depois do carregamento completo da página. Isto costuma ser feito
chamando o construtor GGeoXml no manipulador onload do <body>:
// A API do Google Maps API determina implicitamente se o ficheiro é //KML ou GeoRSS. var map; var geoXml; function initialize() { if (GBrowserIsCompatible()) { map = new GMap2(document.getElementById("map_canvas")); geoXml = new GGeoXml("http://localhost/gmaps/teste.kml"); map.addControl(new GLargeMapControl()); map.setCenter(new GLatLng(41.875696,-87.624207), 11); map.addControl(new GLargeMapControl()); map.addOverlay(geoXml); } }
Ilustração 16 - Sobreposição de GeoRSS/KML
43
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9.8. Rotas
É possível adicionar a funcionalidade de calcular rotas entre GDirections. O objecto
GDirections solicita e recebe resultados de rotas usando strings de consulta (por exemplo,
"Guimarães, Portugal para Montalegre, Portugal") ou latitudes/longitudes textuais (por exemplo,
"40.712882, -73.967257 a 41.943181,-87.770677"). O objecto GDirections também
oferece suporte a rotas de vários pontos usando uma série de waypoints. As rotas podem ser
exibidas como uma polyline que desenha uma rota num mapa ou como série de descrições
textuais num elemento <div> (por exemplo, "Vire à direita na saída da EN103"), ou ambas.
Para usar rotas na API do Google Maps, cria-se um objecto do tipo GDirections e designa-se
um objecto GMap2 e/ou <div> para receber e exibir resultados. Por defeito, o mapa é
centralizado e vinculado pela(s) rotas(s) retornada(s), embora isto possa ser alterado com
parâmetros num objecto GDirectionOptions.
2.9.8.1. Como carregar rotas
As rotas são solicitadas usando o método GDirections.load(). Este método usa uma string
de consulta e um conjunto de parâmetros GDirectionsOptions opcionais. Estão disponíveis
as seguintes opções:
• locale especifica o idioma a ser usado para retornar os resultados, substituindo o
parâmetro da API do Google Maps hl, se fornecido. Se nem o parâmetro locale nem
o hl forem especificados, será usado o idioma padrão do browser.
• travelMode especifica o método de transporte a usar para calcular resultados.
• avoidHighways especifica que as auto-estradas devem ser evitadas ao calcular rotas.
• getPolyline especifica que o objecto de rotas deve retornar dados de polyline para
desenhar as rotas retornadas. Por defeito, o objecto GDirections só retorna dados de
polyline se houver um objecto de mapa para exibi-los.
• getSteps especifica que o objecto de rotas deve retornar instruções textuais de rotas,
mesmo que nenhum contentor <div> seja fornecido para exibir estas instruções.
• preserveViewport especificam que o mapa não deve automaticamente centralizar e
aplicar zoom na caixa delimitadora das rotas retornadas. Em vez disso, o mapa
permanecerá centralizado na janela de visualização actual.
44
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
2.9.8.2. Modos de transporte
Por defeito, pressupõe-se que as rotas sejam rotas de tráfego, embora possamos solicitar outros
modos de transporte passando um GTravelMode ao chamar o método Directions.load().
São suportados os seguintes modos de transporte:
• G_TRAVEL_MODE_DRIVING indica rotas de tráfego padrão usando a malha de
transporte rodoviário
• G_TRAVEL_MODE_WALKING solicita instruções a pé através de caminhos de pedestres
e calçadas, quando disponíveis.
2.9.8.3. Manipular as rotas geradas
Se o objecto GDirections tiver sido construído com um objecto GMap2 fornecido, as rotas
retornadas conterão uma sobreposição de polyline. Se o objecto GDirections tiver sido
construído com um elemento <div> fornecido, as rotas retornadas conterão um objecto
GRoute, com um conjunto de objectos GStep. Se as rotas forem multiponto, as rotas
retornadas irão conter vários objectos GRoute, cada um consistindo numa série de objectos
GStep.
Depois de as rotas serem retornadas, por defeito, o mapa exibirá uma polyline mostrando a rota,
enquanto instruções textuais da rota serão mostradas no <div> fornecido para este propósito.
O objecto GDirections também armazenará internamente resultados que podem ser
recuperados usando os métodos GDirections.getPolyline() e/ou
GDirections.getRoute(i:Number).
As etapas de uma rota podem ser recuperadas usando o método
GRoute.getStep(i:Number), e o resumo HTML desta etapa pode ser recuperado usando
GStep.getDescriptionHtml().
45
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
O objecto GDirections também dispara três eventos que podem ser interceptados:
• "load": Este evento é despoletado quando um resultado de rota é retornado com êxito,
mas antes que quaisquer elementos de sobreposição sejam adicionados ao
mapa/painel.
• "addoverlay": Este evento é disparado depois dos componentes das instruções
textuais das rotas e/ou de polylines forem adicionados ao mapa e/ou a elementos DIV.
• "error": Este evento é disparado se uma solicitação de rotas resultar em erro. Pode-se
usar GDirections.getStatus() para saber mais sobre o erro.
// Criar um objecto directions e registar um mapa e DIV para receber // as rotas geradas var map; var directionsPanel; var directions; function initialize() { map = new GMap2(document.getElementById("map_canvas")); directionsPanel = document.getElementById("my_textual_div"); map.setCenter(new GLatLng(49.496675,-102.65625), 3); directions = new GDirections(map, directionsPanel); directions.load("from: Guimarães, Portugal to: Piscinas Municipais, Montalegre, Portugal"); }
Ilustração 17 - Mapa com rota de viagem
46
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Desenvolvimento do Sistema
Depois de tomadas as decisões quanto às tecnologias a utilizar e explorar/estudar as
possibilidades da API do Google Maps, foi posto em prática o conhecimento resultante desse
estudo e iniciaram-se as etapas de desenvolvimento do sistema.
3. Modelação de Dados Em qualquer projecto que envolva bases de dados relacionais devemos em primeiro lugar
(depois de termos os requisitos do sistema) elaborar o modelo da base de dados. Esta
modelação deve ser a base sólida de todas as outras camadas.
Note-se que a modelação inclui a interacção com as tabelas já existentes no sistema de gestão
de conteúdos criado anteriormente, fazendo uso de algumas das particularidades do modelo de
dados desse mesmo sistema.
Nota: No diagrama abaixo apenas são mostrados, em cada tabela, os campos relevantes para
construir as relações entre elas.
3.1. Modelo ER (entidades e relacionamentos)
Ilustração 18 - Modelo ER da Base de Dados
47
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
3.2. Descrição das Tabelas da Base de Dados
Tabela Descrição
freguesias Tabela com informação textual sobre cada freguesia. Campos relevantes para
a aplicação: contêm as coordenadas da sede de freguesia. Esta tabela já
existia no CMS.
lugares Contém as informações sobre os lugares de cada freguesia. Tem como FK
(foreign key) o Id da freguesia. Contém as coordenadas de cada lugar. Esta
tabela já existia no CMS.
tipo_localizacoes Esta tabela contém os tipos de localizações. Estes tipos de localizações
podem ser criados dinamicamente e permitir colocar diferentes marcadores
no mapa, por exemplo: Restaurantes, Hotéis, Bibliotecas, Museus, etc. Estão
ainda classificadas segundo a sua utilidade:
'dormir','comer','divertir','geral'; permitindo gerar mapas
por estas utilidades. Ao criar um tipo de localização vamos carregar também
um ícone personalizado para lhe associar.
localizacoes Esta é a tabela central de todo o sistema: permite-nos guardar os pontos de
interesse que vamos inserindo no mapa. As localizações estarão classificadas
por tipo (ligação á tabela tipo_localizacao) mas também por lugar,
permitindo uma compartimentação fácil dos pontos por freguesia, por
exemplo. Qualquer localização está ligada a algum conteúdo (o marcador
remete para esse conteúdo), podendo ser uma notícia, página, etc Esta
ligação é feita de um modo especial pelas duas tabelas seguintes.
tipos_documento Esta tabela é explicada no ponto Particularidades do projecto do modelo
de dados. Aí é explicado a forma particular que permite ligar os pontos a
qualquer tipo de conteúdo. Esta tabela já existia no CMS.
docs Esta tabela não existe, só foi usada para exemplificar a relação com os
diversos tipos de conteúdo. Também se entende o funcionamento no ponto
Particularidades do projecto do modelo de dados.
48
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
3.3. Dicionário de dados CREATE TABLE `freguesias` ( `Id` int(6) unsigned NOT NULL auto_increment, `nome` varchar(55) default NULL, `lat` varchar(254) default NULL, `lon` varchar(254) default NULL, `pagina` longtext, `pagina_en` longtext, `pagina_fr` longtext, `hits` int(11) default '0', PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED; CREATE TABLE `lugares` ( `Id` int(6) unsigned NOT NULL auto_increment, `Id_freguesia` tinyint(4) NOT NULL default '0', `nome` char(65) default NULL, `cod_postal` char(10) character set latin1 default '5470', `lat` char(254) character set latin1 default NULL, `lon` char(254) character set latin1 default NULL, PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 ROW_FORMAT=FIXED; CREATE TABLE `tipo_localizacoes` ( `Id` bigint(20) unsigned NOT NULL auto_increment, `nome` char(255) default NULL COMMENT 'Nome', `utilidade` enum('dormir','comer','divertir','geral') default 'comer' COMMENT 'Utilidade', `icone` char(255) NOT NULL default '' COMMENT 'Latitude', PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 ROW_FORMAT=FIXED COMMENT='Tipos de Objectos georeferenciados'; CREATE TABLE `localizacoes` ( `Id` bigint(20) unsigned NOT NULL auto_increment, `nome` varchar(255) default NULL COMMENT 'Nome', `descricao` blob COMMENT 'Pequeno texto descritivo', `lat` varchar(65) NOT NULL default '' COMMENT 'Latitude', `lon` varchar(65) default NULL COMMENT 'Longitude', `tipo` int(8) default NULL COMMENT 'Tipo de Localizacao', `lugar` int(6) default NULL COMMENT 'Aldeia', `tipo_doc` varchar(10) default NULL COMMENT 'tipo de documento relacionado', `Id_doc` int(8) default NULL COMMENT 'Id do documento relacionado', PRIMARY KEY (`Id`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Georeferenciacao'; CREATE TABLE `tipos_documento` ( `tipo_doc` char(4) NOT NULL default 'DF' COMMENT 'Chave primaria', `nome` char(20) default 'DEFAULT' COMMENT 'nome do documento', `tabela` char(25) default NULL, `campo_titulo` char(25) default NULL, PRIMARY KEY (`tipo_doc`) ) ENGINE=InnoDB DEFAULT CHARSET=latin1 COMMENT='Tipos de documentos
disponiveis';
49
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
3.4. Particularidades da Modelação de Dados Esta base de dados tem algumas particularidades que fazem com que seja difícil entender o
esquema conceptual e relacional sem algumas explicações.
Nos tipos de conteúdos (tipos de documento), é onde a base de dados tem as suas
particularidades: Em primeiro lugar, foi criada uma tabela chamada tipos_documento, com
os seguintes campos: tipo_doc (chave primária), nome, tabela, campo_titulo.
O tipo_doc é um código de dois caracteres único para cada documento, depois o nome do
documento, o nome da tabela, onde se encontram armazenados esses documentos e qual o
campo da tabela que lhe dá titulo:
Esta tabela é uma das chaves para o funcionamento do sistema de gestão de conteúdos. A partir
do momento em que se criaram siglas para os diversos tipos de conteúdos, elas foram também
usadas para nomear os scripts php que servem para manipular esses tipos de conteúdo. Esses
nomes foram compostos a partir da sua chave tipo_doc. Por exemplo o script para retornar os
dados de uma notícia e apresentá-los tem o nome de showNT.php, tal como o de mostrar uma
informação tem o nome de showIF.php, ou o de mostrar um regulamento tem o nome de
showRG.php. Tal como o de listar as notícias tem o nome de listNT.php, ou o script para
editar tem o nome editNT.php, o que elimina deleteNT.php e assim sucessivamente para
todas as siglas e respectivos tipos de conteúdo.
Isto permite gerar links para qualquer documento a partir da tabela localizacoes
passando os valores guardados nas entradas dessa mesma tabela. Por exemplo, um ponto geo-
referenciado associado à Página com o Id 123 terá nos campos Id_doc e tipo_doc
respectivamente os valores “123” e “PG” permitindo gerar facilmente um link do tipo
http://www.cm-montalegre.pt/showPG.php?Id=123.
50
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4. Mapa Interactivo Depois de definido o modelo de dados, iniciei o desenvolvimento do mapa interactivo. O front-
end é composto por diversas camadas de software que em conjunto geram o mapa, os
marcadores e as info-windows (janelas de informações) associadas a cada ponto.
4.1. Front-end do mapa A parte visível (aos utilizadores) do mapa depende de várias camadas de software para ser
gerada.
Estas camadas interagem entre si de modo a consultar os dados da BD, processá-los, gerar o
ficheiro XML de marcadores e as info-windows. Na figura seguinte podemos ver um esquema de
interacção entre as camadas:
Ilustração 19 - Camadas do Front-End
Javascript + HTML + CSS
(API Google Maps)
XMLHttpRequest
PHP
MySQL
XML
XMLHttpRequest
51
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Antes de detalhar a implementação, vamos ver o aspecto e funcionalidades do front-end de um
dos mapas gerados:
Ilustração 20 - Exemplo de mapa gerado
52
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Detalhe da info-window:
Ilustração 21 - Mapa com Info-Window
Aba da info-window que permite gerar rota até ao ponto escolhido:
Ilustração 22 - Aba para geração da rota
As rotas podem ser geradas tendo o marcador tanto como ponto de partida ou como ponto de
chegada.
Ilustração 23 - Exemplo de rota
53
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Além do trajecto desenhado sobre o mapa, a geração de rotas contempla ainda a geração
descritiva do trajecto:
Ilustração 24 - Listagem descritiva do trajecto
Os tabs na parte superior do mapa implementam na prática o que foi descrito na modelação de
dados como a “utilidade” de cada um dos tipos de marcadores implementados: comer, dormir,
divertir e geral:
Ilustração 25 - Tabs superiores do mapa
54
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Estas tabs actualizam o mapa e também a lista descritiva dos marcadores, usando AJAX, ou seja
sem recarregar a página.
Ilustração 26 - Mapa com o tema "Onde Dormir"
A listagem dos marcadores também é actualizada assincronamente. Esta lista permite, ao
clicar, localizar o marcador no mapa, abrindo a respectiva info-window:
Ilustração 27 - Marcador aberto pelo link da lista descritiva
55
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.1. Interface do Mapa (HTML + JavaScript e CSS)
Para gerar o mapa, precisamos primeiro de delinear o layout da página (HTML+CSS),
respeitando o design já incluído no site. Precisamos depois de carregar o JavaScript necessário
para implementar a comunicação com a API Google Maps.
Tal como já tinha explicado nas experiências feitas com a API, no head da página precisamos de
carregar o script da API e acrescentar a função GUnload()ao evento onunload do body :
<script src="http://maps.google.com/maps?file=api&v=2.x&key=xxxxxxxxxxxxxxxxx" type="text/JavaScript"></script> <body onunload="GUnload()">
Seguidamente adicionamos os contentores que vão receber o mapa, e no meu caso também um
contentor para a lista de marcadores e outro para as rotas:
<table style="border-color:#999999; border-width:1px; border-style:solid;" > <tr> <td> <div id="map" style="width: 685px; height: 600px; overflow:visible"></div> </td> </tr> <tr> <td width=685 valign="top" style="border-color:#999999; border-width:1px; border-style:solid;"> <div id="side_bar" style="color: #4444ff; line-height:15px; height:auto; overflow:auto;"></div> <div id="directions" style="overflow:auto; height:auto; visibility:hidden"> <hr color="#999999" style="border-color:#999999; color:#999999;"> <br /> </div> <div align="right"> <input type="button" id="botaolimpa" class="input2" name="botaolimpa" onclick="limpa_rota()" value="Limpar Rota" disabled="disabled" style="visibility:hidden;" /> </div> </td> </tr> </table>
Neste caso a div que vai receber os mapas tem o identificador (id) “map”, a que recebe a lista de
pontos tem o id “side_bar” (que por acaso não fica lateral - “side_bar” ao mapa: na versão
final fica por baixo do mapa, mas como já tinha muito código com esta nomenclatura e isto não
me atrapalha nada, deixei ficar) e a que recebe os pontos o id “directions”.
O mapa reutiliza o CSS geral da página, contendo alguns estilos inline aplicados aos contentores.
56
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ainda no capítulo do interface, temos as tabs que se encontram por cima do mapa e que
permitem carregar os pontos pelos diversos tipos de utilidade: Onde Comer, Onde Dormir,
Pontos de Interesse/Diversão e Localizações Úteis.
<div class="borders_tab" id=comer onclick='mapa_pontos("http://www.cm-montalegre.pt/gmaps/get_pontos_geral.php?&util=comer", 41.79179268262892, -7.86895751953125, 11, "Onde Comer:")' > Onde Comer</div> <div class="borders_tab" id=dormir onclick='mapa_pontos("http://www.cm-montalegre.pt/gmaps/get_pontos_geral.php?&util=dormir", 41.79179268262892, -7.86895751953125, 11, "Onde Dormir:")' > Onde Dormir</div> <div class="borders_tab" id=divertir onclick='mapa_pontos("http://www.cm-montalegre.pt/gmaps/get_pontos_geral.php?&util=divertir", 41.79179268262892, -7.86895751953125, 11, "Pontos de Interesse/Diversão:")' > Pontos de Interesse/Diversão</div>
<div class="borders_tab" id=geral
onclick='mapa_pontos("http://www.cm-
montalegre.pt/gmaps/get_pontos_geral.php?&util=geral",
41.79179268262892, -7.86895751953125, 11, "Localizações Úteis:")'>
Localizações Úteis</div>
Ao clicar em cada um destes tabs é chamada uma função JavaScript chamada
mapa_pontos(), a qual leva como parâmetros o ficheiro XML de marcadores, a latitude e
longitude do centro do mapa, o nível de zoom e o nome do conjunto de pontos:
mapa_pontos("http://www.cm-
montalegre.pt/gmaps/get_pontos_geral.php?&util=comer",
41.79179268262892, -7.86895751953125, 11, "Onde Comer:")
Esta é a função que invoca o XMLHttpRequest(), sendo a função nuclear de todo o front-end
do mapa.
Deixando a formatação do interface em si, temos um conjunto de funções auxiliares em
JavaScript que são necessárias para a interligação à camada de dados, mas também para
implementar as funcionalidades dos controlos disponíveis ao utilizador.
57
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.1.1. Inicializações Básicas (JavaScript – API)
// variável para guardar html da lista de pontos e array para guardar os marcadores var side_bar_html = ""; var gmarkers = []; // arrays que guardam as variantes do html da tab principal da info-window var htmls = []; var names = []; // arrays que guardam as variantes do html da tab rotas da info-window var htmls2 = []; // contador var i = 0; // arrays que guardam as variantes do html da info-window quando o formulario das rotas está aberto var to_htmls = []; var from_htmls = []; // criar o mapa var map = new GMap2(document.getElementById("map")); //limites do concelho em kmz * explicação detalhada abaixo geoXml2 = new GGeoXml("http://www.cm-montalegre.pt/gmaps/limites.kmz"); // === criar objecto GDirections para rotas === var gdir = new GDirections(map, document.getElementById("directions")); // === Array para descodificar códigos de erro === var reasons=[]; reasons[G_GEO_SUCCESS] = "Successo"; reasons[G_GEO_MISSING_ADDRESS] = "Morada em Falta: A morada ou não é encontrada ou tem um valor errado. (Insira: Localidade, Concelho, País)"; reasons[G_GEO_UNKNOWN_ADDRESS] = "Morada com poucos dados: Mais do que uma morada encontarada com esse nome. (Insira: Localidade, Concelho, País)"; reasons[G_GEO_UNAVAILABLE_ADDRESS]= "Morada não disponível: As coordenadas dessa morada não podem ser revelados por questões legais."; reasons[G_GEO_BAD_KEY] = "API Key: API key invalida ou nao e para este site/dominio"; reasons[G_GEO_TOO_MANY_QUERIES] = "Demasiados Pedidos: A quota de pedidos de georeferenciacao pra este site ultrapassou o maximo diario."; reasons[G_GEO_SERVER_ERROR] = "Erro de Servidor: O Pedido nao pode ser processado."; reasons[G_GEO_BAD_REQUEST] = "O pedido de rota nao foi processado com sucesso."; reasons[G_GEO_MISSING_QUERY] = "Nao especificou nenhuma morada para pesquisar."; reasons[G_GEO_UNKNOWN_DIRECTIONS] = "Impossivel gerar rota entre os dois pontos. (Insira: Localidade, Concelho, País)";
58
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.1.2. Funções auxiliares JavaScript do front-end
//===Função que permite criar um marcador – parâmetros incluem //coordenadas do ponto, nome, html da tab 1 (default) e tab 2 (rotas) // e também o ícone==== function createMarker(point,name,html1,html2,icone) { markerOptions = { icon:icone }; var marker = new GMarker(point, markerOptions); // Versão da info-window com o formulário "Daqui" aberto to_htmls[i] = html2 + '<br>Trajecto: <b>Para Aqui</b> - <a href="JavaScript:fromhere(' + i + ')">Daqui</a><br>' + '<br>Ponto de Partida:<form action="JavaScript:getDirections()">' + '<input type="text" SIZE=40 MAXLENGTH=40 name="saddr" id="saddr" value="" /><br>' + '<INPUT value="Ver Rota" TYPE="SUBMIT">' + '<input type="hidden" id="daddr" value="'+name+"@"+ point.lat() + ',' + point.lng() + '"/>'; // Info-window com o formulário "Para Aqui" aberto from_htmls[i] = html2 + '<br>Trajecto: <a href="JavaScript:tohere(' + i + ')">Para Aqui</a> - <b>Daqui</b><br>' + '<br>Ponto de Chegada:<form action="JavaScript:getDirections()">' + '<input type="text" SIZE=40 MAXLENGTH=40 name="daddr" id="daddr" value="" /><br>' + '<INPUT value="Ver Rota" TYPE="SUBMIT">' + '<input type="hidden" id="saddr" value="'+name+"@"+ point.lat() + ',' + point.lng() + '"/>'; // Versão inactiva da informação de rotas html2 = html2 + '<br>Trajecto: <a href="JavaScript:tohere('+i+')">Para aqui</a> - <a href="JavaScript:fromhere('+i+')">Daqui</a><br>'; GEvent.addListener(marker, "click", function() { //marker.openInfoWindowHtml(html2); marker.openInfoWindowTabsHtml([new GInfoWindowTab(name,html1), new GInfoWindowTab("Rota",html2)]); }); // guardam os dados que vamos precisar para gerar a side_bar gmarkers[i] = marker; htmls[i] = html2; names[i] = name; htmls2[i] = html1; // addicionar seta ao html da side_bar e link javascrip para abrir info-window side_bar_html += '<img src="images/seta_small.gif" hspace="3" align="absmiddle"><a href="JavaScript:myclick(' + i + ')">' + name + '</a><br>'; i++; return marker; }
59
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
// === Função para ouvir Erros do objecto Directions === GEvent.addListener(gdir, "error", function() { var code = gdir.getStatus().code; var reason="Code "+code; if (reasons[code]) { reason = reasons[code] } alert("Trajecto não encontrado: "+reason); }); // ===== Função que faz o pedido de rotas ao google===== function getDirections() { var saddr = document.getElementById("saddr").value var daddr = document.getElementById("daddr").value document.getElementById("botaolimpa").disabled = false; document.getElementById("botaolimpa").style.visibility = "visible"; document.getElementById("directions").style.visibility = "visible"; gdir.load("from: "+saddr+" to: "+daddr); } // == Esta função abre as info windows respectivas dos marcadores == function myclick(i) {
gmarkers[i].openInfoWindowTabsHtml([new GInfoWindowTab(names[i],htmls2[i]), new GInfoWindowTab("Rota",htmls[i])]);
} // funções que abrem os formulários das rotas daqui e para aqui function tohere(i) {
gmarkers[i].openInfoWindowTabsHtml([ new GInfoWindowTab("Rota",to_htmls[i]), new GInfoWindowTab(names[i],htmls2[i]) ]);
//map.getInfoWindow().selectTab(1); } function fromhere(i) {
gmarkers[i].openInfoWindowTabsHtml([new GInfoWindowTab("Rota",from_htmls[i]), new GInfoWindowTab(names[i],htmls2[i])]);
//map.getInfoWindow().selectTab(1); } //limpar a rota do mapa function limpa_rota(){ gdir.clear(); map.returnToSavedPosition(); document.getElementById("botaolimpa").disabled = true; document.getElementById("botaolimpa").style.visibility = "hidden"; document.getElementById("directions").style.visibility = "hidden"; }
60
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Todas as funções descritas anteriormente servem para interagir com o mapa (como podemos
verificar nos comentários explicativos), mas nenhuma delas tem interacção com a camada de
dados. Isso vai ser feito pela função que descrevemos a seguir, que faz uso de chamadas a
todas elas.
4.1.1.3. Chamadas à camada de dados – XM LHttpRequest()
Todo o código visto anteriormente prepara/ajuda a preparar o mapa para receber o seu
elemento mais importante: os marcadores. Tudo gira em torno da função seguinte: a função
mapa_pontos(). Esta função faz as chamadas às camadas abaixo e recebe as respostas.
Recorre depois às funções auxiliares já descritas acima para formatar e dar funcionalidades aos
dados em bruto recebidos através do mecanismo XMLHttpRequest(). Toda a função está
explicada nos comentários ao código, e cujas variáveis de input já forma explicadas
anteriormente:
function mapa_pontos(dados, lat, lon, zoom, points_title){ //limpa o mapa, criando um objecto novo. map = new GMap2(document.getElementById("map")); // por controlos de zoom e panning no mapa map.setUIToDefault(); map.addControl(new GHierarchicalMapTypeControl()); map.setCenter(new GLatLng(lat,lon), zoom); // criar um objecto GDirections (rotas) e atribuir-lhe a div de destino “directions” gdir = new GDirections(map, document.getElementById("directions")); // inicializar as variáveis que vão receber o html das tabs, e da side_bar side_bar_html = "<b><font color=#000000>"+points_title+"</font></b><br /><br />"; gmarkers = []; htmls = []; names = []; htmls2 = []; i = 0; // arrays que guardam as variantes do html da info-window quando o formulario das rotas está aberto to_htmls = []; from_htmls = []; //testar se o browser tem capacidade para a API if (GBrowserIsCompatible()) {
61
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//icones por defeito das freguesias (usado só nos mapas das freguesias) var icon = new GIcon(); icon.image = "http://www.cm-montalegre.pt/gmaps/markers/sede_freg.png"; icon.shadow = "http://www.cm-montalegre.pt/gmaps/markers/shadow2.png"; //tamanho dos icones e das ancoras das janelas icon.iconSize = new GSize(30, 30); icon.shadowSize = new GSize(45, 30); icon.iconAnchor = new GPoint(15, 30); icon.infoWindowAnchor = new GPoint(15, 30); // Método da API do Google Maps para criar uma instância do XMLHttpRequest, independentemente do browser. var request = GXmlHttp.create(); //chamada ao XMLHttpRequest usando método GET e o endereço do ficheiro de dados passado em parâmetro na função mapa_pontos request.open("GET", dados, true); //usamos um event listener que vai disparar a função de processamento dos dados retornados quando o estado do pedido muda: "onreadystatechange" request.onreadystatechange = function() { // se o pedido está no estado 4 (carregado com sucesso) vamos processar o XML retornado if (request.readyState == 4) { // carregar os dados XML numa variável var xmlDoc = GXml.parse(request.responseText); // obter o array de marcadores e percorre-lo var markers = xmlDoc.documentElement.getElementsByTagName("marker"); for (var i = 0; i < markers.length; i++) { // obtem os atributos de cada marcador var lat = parseFloat(markers[i].getAttribute("lat")); var lng = parseFloat(markers[i].getAttribute("lng")); var point = new GLatLng(lat,lng); var html = markers[i].getAttribute("html"); var label = markers[i].getAttribute("label"); var icone = markers[i].getAttribute("icone"); var size = markers[i].getAttribute("size"); //gerar os ícones e html das info-windows através dos dados lidos do XML icon.iconSize = new GSize(size, size); icon.image = "http://www.cm-montalegre.pt/gmaps/markers/"+icone;
62
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
icon.shadow = "http://www.cm-montalegre.pt/gmaps/markers/shadow.png"; //Chama a função createMarker, que além de criar o marcador, gera as info-windows, rotas, etc... var marker = createMarker(point,label,html,"",icon); //ADICIONA EFECTIVAMENTE O MARCADOR AO MAPA map.addOverlay(marker); } // por o html assemblado na variavel side_bar_html (atenção que isto é feito na função createMarker)aqui só é insertido na div “side_bar” document.getElementById("side_bar").innerHTML = side_bar_html; } } // no caso de o estado não ser 4... request.send(null); } //browser nao compatível, mensagem de erro else { alert("Desculpe, os mapas Google Maps nao são compatíveis com este browser"); } // guardar posição do mapa map.savePosition(); }
Algumas considerações/explicações adicionais sobre o código acima:
Nem todos os browsers oferecem suporte à API do Google, e nem todos os utilizadores têm o
JavaScript activado. Foi usado um elemento <noscript> para detectar a situação em que não
há JavaScript e uso GBrowserIsCompatible () para detectar um browser não compatível.
A função createMarker() não só cria o marcador e o seu manipulador de eventos, mas
também faz com que cópias locais do "marcador" e variáveis "html" sejam preservados para
uso posterior quando os eventos forem accionados. Esta é a característica especial do JavaScript
já explicado anteriormente chamada "function closure".
Podemos usar quase qualquer html válido nas info-windows, desde que o browser possa calcular
a altura e a largura necessária antes da apresentação.
63
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Em todas as situações em que foi usado html que precisa de aspas, foi necessário usar aspas
simples na string de JavaScript e aspas normais no html interno.
4.1.1.4. Problemas que surgiram durante esta fase
Todas estas situações aparentemente triviais tiveram resoluções algo demoradas e várias
iterações de tentativa/erro antes de chegar a estas conclusões:
1. O conteúdo da info-window herda a formatação do elemento pai. Se o <div> do mapa
for posicionado com <center>, então o conteúdo da janela de informação será
centralizado.
2. O código que decide o tamanho da info-window é executado antes da herança de estilos
CSS ocorrer. Se o conteúdo da janela de informação, em seguida, herdar estilos que
alterem o seu tamanho, por exemplo, font-size, o tamanho da janela de informação
pode não chegar para todo o conteúdo que contém. Ainda não foi encontrada uma
solução satisfatória para este problema…
3. Não colocar JavaScript dentro de uma <table> ou <div>, funciona no Firefox, mas
não no Internet Explorer. Onde colocar o JavaScript: pouco antes do </body>, e no
header dentro de uma função onLoad().
4. Esta foi de difícil descoberta e resolução: Numa função onLoad() não se pode ter o
nome "onload()" em minúsculas, porque é uma palavra reservada no Firefox…
5. Para usar tabelas ou divs dentro de uma info-window, é necessário especificar a largura
e altura. Se usarmos "width = 100%", o browser não é capaz de calcular o tamanho
da janela antes da apresentação. Isto dá resultados um bocado estranhos, desde janelas
que não aprecem, até aos tamanhos mais disparatados de janela.
6. Verificar que o ID do div do mapa é único. Ter um hyperlink (por exemplo para o mapa
do site, como neste caso) em que o id = "map" gera confusão, demorando a chegar à
causa do problema, pois é uma das coisas em que não se pensa facilmente.
64
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.2. Camadas PHP, MySQL e XML
Estas três camadas são implementadas com um script PHP que é chamado pela função
mapa_pontos, através do XMLHttpRequest(). Este script, conciso e elegante, faz a consulta
à base de dados e gera, como resposta, um ficheiro XML “on-the-fly” com os marcadores para
serem processados pelo JavaScript do mapa. Todos os detalhes estão nos comentários ao
código:
<markers> <?php // importar variaves do endereço import_request_variables("gP"); // Configurar a ligação ao MySQL $MySqlHostname = "localhost"; //nome da mquina $MySqlUsername = "xpto_user"; //login $MySqlPassword = "xpto"; //password $MySqlDatabase = "xpto_db"; //base de dados $dblink=MYSQL_CONNECT($MySqlHostname, $MySqlUsername, $MySqlPassword) OR DIE("Falhou ligacao a base de dados."); //efectuar ligação @mysql_select_db("$MySqlDatabase") or die( "Impossvel escolher base de dados."); //Query SQL à base de dados construído dinamicamente a partir das variáveis de endereço $query = " select * from localizacoes where tipo in ( SELECT Id FROM tipo_localizacoes where utilidade = '$util' ) ORDER BY nome ASC "; $query = mysql_query($query); // Percorrer o array de dados while ($row=mysql_fetch_assoc($query)){ // Query para saber o ícone associado a cada ponto, localizado na tabela de tipo_localizacoes $res = mysql_query(" select * from tipo_localizacoes where Id = ".$row['tipo']." "); $icone = mysql_result($res,0,"icone"); //gerar o html para linkar á pgina de detalhe do marcador... // todas as entidades htnl estão codificadas (> < etc) $row['descricao'] = $row['descricao']."<br><br><a href=show".$row['tipo_doc'].".php?Id=".$row['Id_doc']." target=_blank >Mais detalhes »»</a>"; //substituir os new lines por codigo html <br> $html = str_replace("\n","<br>", $row['descricao']); ?> <!-- gerar um row XML de um marcador --> <marker lat="<?php echo $row['lat']; ?>" lng="<?php echo $row['lon']; ?>" html="<?php echo $html; ?>" label="<?php echo $row['nome']; ?>" icone="<?php echo $icone; ?>" size="35" /> <? } ?> </markers>
65
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.2.1. Exemplo de output XML gerado
Este é o XML gerado quando se chama o script desta forma:
http://www.cm-montalegre.pt/gmaps/get_pontos_geral.php?&util=comer
Resultado: <markers> <marker lat="41.72072119663713" lng="-7.885565757751465" html="Vila da Ponte <br>5470 – 543 Vila da Ponte <br>Telef.: 276556235<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="A CISTA" icone="restaurante.png" size="35" /> <marker lat="41.82512431907592" lng="-7.7904438972473144" html="Rª do Reigoso <br>5470 – 238 Montalegre <br>Telef.: 276512240<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="A MURALHA" icone="restaurante.png" size="35" /> <marker lat="41.82461264002405" lng="-7.792525291442871" html="Rª D. Afonso III <br>5470 – 241 Montalegre <br>Telem.935125010<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="ADEGA O FUMEIRO" icone="restaurante.png" size="35" />
… <marker lat="41.82522025844289" lng="-7.792160511016846" html="Rª dos Ferradores <br>www.djoao.com <br>5470 – 242 Montalegre <br>Telef.:276511388<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="D. JOÃO" icone="restaurante.png" size="35" /> <marker lat="41.8417748712" lng="-7.9507951436" html="Pitões <br>5470 – 370 Pitões das Júnias <br>Telef.: 276566288<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="D. PEDRO" icone="restaurante.png" size="35" /> <marker lat="41.75605868491745" lng="-7.848787307739258" html="Parafita <br>5470 – 525 Viade de Baixo <br>Telef.: 276556123<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="DIAS PARAFITA" icone="restaurante.png" size="35" /> <marker lat="41.82115869946618" lng="-7.7931904792785644" html="Rª do Avelar <br>5470 – 235 Montalegre <br>Telef.: 276512664<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="FALTA D’AR" icone="restaurante.png" size="35" /> <marker lat="41.7638879919" lng="-7.9461232579" html="Paradela do Rio <br>5470 – 362 Paradela do Rio <br>Telef.: 276566122<br><br><a href=showPG.php?Id=119 target=_blank >Mais detalhes »»</a>" label="FLOR DO RIO" icone="restaurante.png" size="35" /> </markers>
66
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
4.1.2.2. Problemas que surgiram durante esta fase
Tal como na fase anterior, existiram nesta situações aparentemente triviais que tiveram
resoluções algo demoradas e várias iterações de tentativa/erro antes de chegar a estas
conclusões:
1. Todos os dados retornados do XML são considerados como strings.
2. Evitar, no XML, nomes de atributos que são palavras reservadas em JavaScript ou
HTML. Por exemplo "name" é uma palavra reservada…
3. Strings de dados XML não podem conter os caracteres “&”, “<” ou “>”. Temos que usar
&, < e >, que serão convertidos no html para “&”, “<” e “>”.
4. Não se pode ter linhas em branco ou espaços antes da primeira tag no ficheiro XML.
5. Não confundir a matriz de objectos marcador "gmarkers []" e o array de dados html dos
marcadores "markers[]"…
6. Para ver o resultado da geração do ficheiro XML tem que se utilizar o comando "ver
código fonte" do browser.
4.2. Mapa das freguesias: um mapa com particularidades
Ilustração 28 - Mapa de uma freguesia com as linhas divisórias entre as freguesias e tabs temáticas
67
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Depois de ter todo o trabalho concluído para os mapas turísticos, foi reaproveitado o código para
gerar os mapas incluídos na plataforma das freguesias do site. Estes mapas reaproveitam todos
os dados turísticos gerais, filtrando apenas os que pertencem a cada freguesia em particular.
Adicionam também mais um tema ao mapa, o “Mapa da Freguesia” que contém marcadores de
todas as localidades da freguesia e a indicação da sede com um marcador diferente, como pode
ser visto acima, na ilustração 28.
Ilustração 29 - Gerar uma rota até um lugar de uma freguesia
É possível também gerar rotas até às localidades, tal como nos mapas anteriores.
4.2.1. Acrescentar as delimitações das freguesias ao mapa
A partir do momento em que se decidiu utilizar mapas nas páginas das freguesias, isso só faria
sentido apresentando as linhas que delimitam essa freguesia.
Depois de todo o estudo feito com a API, a solução óbvia seria sobrepor um ficheiro KML que
contivesse os polígonos das freguesias. Problema: como o construir?
Tínhamos esses dados disponíveis do Gabinete de SIG do Município de Montalegre, mas em
formato shape-file (shp) da plataforma ArcGis. Além disto, todos os dados geográficos do
município estão no sistema de coordenadas Hayford-Gauss Datum73 e tanto o Google Earth
como o Google Maps operam com o sistema de coordenadasWGS84. A versão do ArcGis que
tínhamos disponível ainda não possuía a funcionalidade de exportar para KML.
Depois de alguma investigação na internet e em bibliografia, encontrou-se uma ferramenta
chamada “SHP2KML”, que é freeware. Além de converter o formato, permite ainda conversão
de coordenadas. À primeira parecia encontrada a solução, mas foi mais tarde verificado que só
parcialmente. A ferramenta tem uma limitação: não consegue converter sistemas de
coordenadas projectadas (como é o caso do Datum73) em sistemas de coordenadas geográficas
(como é o WGS84).
68
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Pelo menos parte do problema estava resolvida: tínhamos os polígonos em KML, mas em
coordenadas Datum73.
Ilustração 30 - Ferramenta SHP2KML
Depois de muita procura, muitos softwares testados e de muitas versões de limites de freguesias
que apareciam em sítios errados do globo, (coordenadas erradas), foi encontrada a ferramenta
que finalmente permitiu converter com sucesso entre os dois sistemas de coordenadas: o
software Franson CoordTrans. Esta ferramenta não é grátis, por isso foi necessário adquirir uma
licença.
Ilustração 31 - Franson CoordTrans
Com esta aplicação foi convertido correctamente o SHP para a posterior transformação no KML
a carregar nos mapas das freguesias, e com sucesso, como pode ser visto na ilustração 28.
69
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
5. Back-Office Para que os mapas sejam criados e geridos dinamicamente necessitamos de um sistema de
back-office que permita gerir de forma eficaz quer a categorização de marcadores quer a sua
associação a conteúdos.
Todo o back-office de georeferenciação foi integrado no CMS (Content Management System –
Sistema de Gestão de Conteúdos) já em produção e portanto a questão de autenticação e
permissões foi assegurada com os mecanismos pré existentes.
Para implementar o back-office foi desenvolvido um sistema CRUD (Create, Read, Update,
Delete) – CRUD são todas as operações básicas que se podem fazer sobre uma base de dados -
baseado em jQuery (AJAX) e PHP, reutilizável com bases de dados MySQL.
Este sistema implementa todas as operações sobre a BD numa só página, recorrendo ao
XMLHttpRequest para efectuar todas as tarefas de criação, leitura, actualização e eliminação
de registos.
5.1. Gerir Tipos de Localização Tal como se pode ver no modelo de dados, os marcadores/pontos estão agrupados em
categorias (“Tipos de Localização”). Estas categorias pertencem ainda a quatro grupo maiores
(utilidade) que são “Onde Comer”, “Onde Dormir”, “Diversão”, “Localizações Úteis”.
Para criarmos marcadores necessitamos primeiro de criar categorias para os agrupar.
Implementou-se a ferramenta que permite gerir os tipos de localização recorrendo a uma grelha
CRUD AJAX que permite fazer todas as operações numa só página.
Esta grelha é editável in-place. Na listagem de tipos de localização, podemos clicar em qualquer
dos campos de um registo para o editar, sem sermos redireccionados para outra página. Ao
clicar, o texto transforma-se num controlo editável (text-box, text-area, select, calendário, etc…)
que nos permite modificar o valor. Ao terminar a edição e clicarmos no botão “OK” respectivo, o
script encarrega-se de guardar o valor na Base de Dados (assincronamente) sem para isso
recarregar absolutamente nada.
O formulário para adicionar novos registos também se encontra na mesma página, sendo
revelado no momento em que clicamos no botão de adição.
70
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Como seria de esperar, todos os controlos básicos sobre a grelha (ordenação por qualquer um
dos campos em ordem ascendente ou descendente, paginação, filtragem in-line, etc.) são
efectuados sem recarregar a página, tornando o interface fluído, rápido, eficaz e menos
cansativo para quem gere os dados. As figuras seguintes ilustram todas estas características.
Ilustração 32 - Grelha CRUD AJAX para gestão dos Tipos de Localização
Ilustração 33 - Formulário in-line para adicionar um novo registo
71
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 34 - Eliminação in-line
Ilustração 35 - Edição in-line
Ilustração 36 - Filtros in-line
72
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 37 - Ordenação in-line
5.2. Georeferenciação de conteúdo A partir do momento em que temos os tipos de localização definidos podemos começar a
georeferenciar conteúdos.
A georeferenciação foi adicionada ao CMS como mais um passo (opcional) aquando da criação
de um conteúdo, seja uma página, uma notícia, regulamento, requerimento, etc…
No passo final da criação do conteúdo temos mais um botão que nos permite criar um marcador
que terá um link que remete para o conteúdo criado.
Ilustração 38 - Botão "GEOREFERENCIAR" na etapa final da criação de um conteúdo
Clicando no botão somos levados para a plataforma de georeferenciação:
73
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 39 - Formulário de Georeferenciação
Na figura acima podemos ver que temos um mapa, o qual nos permite, ao clicar, obter as
coordenadas (Latitude e Longitude) do ponto desejado.
Temos que escolher, de seguida, o tipo de localização associado a este ponto:
Ilustração 40 - Escolha do tipo de localização
Se não se enquadrar em nenhum dos tipos pré-definidos, podemos criar um novo (link do lado
direito da select-box).
De seguida escolhemos a localidade. Inseri este campo (além das coordenadas) para depois
gerar os mapas temáticos por freguesia. Isso poderia ser feito pela intersecção das coordenadas
com os polígonos das freguesias, mas penso que não compensava o nível de complicação, além
da questão da performance – seria querys com tempo de excução elevado.
74
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Ilustração 41 - Selecção da Localidade
Como podemos ver o Título/Nome do ponto é automaticamente importado do título do conteúdo
associado. Este fica editável no caso de necessitarmos de o modificar. Podemos ainda
acrescentar uma pequena descrição que aparecerá na info-window.
Na figura seguinte vemos que ao clicar colocamos um ícone no sítio do click e os campos
latitude e longitude do formulário são automaticamente preenchidos, para inserir na base de
dados.
Para capturar o click e ler as coordenadas foi utilizada uma variante do código explorado para
este efeito durante o estudo da API do Google Maps.
Ilustração 42 - Referenciar um ponto
75
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Cada página pode ter um ou vários pontos associados. O exemplo abaixo mostra os pontos
associados à página de restaurantes:
Ilustração 43 - Vários pontos associados a um conteúdo
Como é óbvio podemos modificar ou apagar qualquer um dos pontos.
Desta forma expedita podemos acrescentar georeferenciação a qualquer conteúdo.
O resultado da georeferenciação já foi visto no capítulo do front-end.
5.3. Utilização da ferramenta CRUD AJAX para gerar o interface do back-of f ice
Como foi explicado no início deste capítulo, foi desenvolvida uma classe para gerar interfaces
CRUD de uma base de dados usando PHP e AJAX (através de jQuery). Para demonstrar a
facilidade com que esta ferramenta pode ser usada, coloco abaixo o código necessário para
gerar a interface de gestão dos Tipos de Localização. Todo o código está explicado nos
comentários:
<?php session_start(); require_once('../preheader.php'); #carregar a classe include ('../ajaxCRUD.class.php'); ?> <html> <head> <meta http-equiv="content-type" content="text/html; charset=utf-8"> <title>Gerir Tipos de Localizações</title> <link href="/default.css" rel="stylesheet" type="text/css"> </head> <body>
76
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
<?php //ver se o utilizador está autenticado if(isset($_SESSION['cod_func'])){ ?> <h2 style="margin:30px;" align="center">Gerir Tipos de Localização</h2> <?php # criar um objecto da classe ajaxCRUD ######################################################## ## $tblLoc = new ajaxCRUD("Tipo de Localização", "tipo_localizacoes", "Id", "../"); #omitir a chave primária #$tblLoc->omitPrimaryKey(); #definir os nomes dos campos na apresentação $tblLoc->displayAs("nome", "Nome"); $tblLoc->displayAs("utilidade", "Utilidade"); $tblLoc->displayAs("icone", "Icone"); #campos data/ input calendário dinâmico #$tblLoc->setData('data_inicio'); #$tblLoc->setData('data_fim'); //definir um campo como file upload $tblLoc->setFileUpload("icone", "../../gmaps/markers/", "http://www.cm-montalegre.pt/gmaps/markers/"); #definir a ordenação inicial $tblLoc->addOrderBy("ORDER BY Id DESC"); // definir este campo como select só de uma lista de valores $allowableValues = array("comer", "dormir", "divertir", "geral"); $tblLoc->defineAllowableValues("utilidade", $allowableValues); // numero de registos por página $tblLoc->setLimit(10); #colocar uma caixa de filtro pelo nome $tblLoc->addAjaxFilterBox('nome'); #tamanho da caixa de filtro $tblLoc->setAjaxFilterBoxSize('nome', 30); #funções para formatar apresentação de dados na tabela //$tblLoc->formatFieldWithFunction('nome', 'utf'); #$tblLoc->formatFieldWithFunction('texto', 'makeBold'); #gerar a tabela $tblLoc->showTable(); ?> <?php
77
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} else{ echo "<h2>Acabou o tempo de sessao, ou o seu browser nao tem os cookies activos!</h2>"; } ?> <br> <br> </body> </html> <?php #funções extra para processar output function makeBold($val){ return "<b>$val</b>"; } function utf($val){ return utf8_encode($val); } function makeBlue($val){ return "$val"; } ?>
Como vimos em poucas linhas de código criamos uma aplicação que permite gerir todos os
dados de uma tabela. Todo o código da classe php ajaxCRUD estará em anexo, pois a sua
explicação ultrapassa o âmbito deste capítulo.
78
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Conclusões
Depois de mais de um ano de trabalho podemos dizer que foram superados os objectivos iniciais
do projecto.
Os mapas, para quem consulta são de fácil navegação e leitura, intuitivos e úteis.
Para quem faz a gestão de conteúdos a plataforma é muito flexível, fácil de utilizar, rápida na
publicação e intuitiva. Permite num curto espaço de tempo georreferenciar muita informação e
compartimentá-la de forma eficaz e acessível.
A gestão de informação pode ser feita por qualquer pessoa com um domínio mínimo de
navegação Internet e Tecnologias de Informação.
Este é um trabalho que além de ser uma tese de mestrado é um sistema que está a ser usado
diariamente online e que é consultado mensalmente por centenas de pessoas, como podemos
constatar neste extracto da análise estatística da página:
Podemos ver no gráfico acima que durante o último ano o mapa foi visitado 8746 vezes o que
dá uma média mensal de 728,83 visitantes, o que é bem significativo.
Os objectivos iniciais do projecto foram claramente atingidos, sendo um projecto com muito
espaço para evoluir.
Essa evolução será a médio prazo e poderá contemplar, entre outros, a introdução de interfaces
AJAX nalgumas partes do back-office que ainda utilizam interfaces Web normais, acrescentar
79
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
novas funções ao front-end como uma lista com as legendas dos marcadores, um filtro para as
categorias de marcadores, a inclusão de “mini-mapas” nas páginas de conteúdos ao invés de
todo o conteúdo georreferenciado estar num só mapa. Depois de aferido o interesse, poderá este
sistema ser integrado com sistema de SIG municipal sendo que este está assente no SGBD
postgreSQL + postGIS (extensão GIS do postgreSQL), implicando a interligação entre dois SGBD
diferentes: MySQL e postgreSQL.
80%, ou mais, dos conhecimentos necessários à realização deste projecto foram adquiridos com
estudo e investigação, pois tanto a API do Google Maps como jQuery eram para o autor
desconhecidos antes da realização deste trabalho.
Além da programação em PHP, JavaScript, HTML, CSS, SQL, da investigação em áreas da
informática ainda não estudadas, foi necessário ainda que investigar na área dos sistemas de
coordenadas, e sistemas de informação geográfica (SIG).
Recomenda-se também para que se tenha uma ideia mais completa da complexidade de
algumas aplicações e de todo o sistema, uma análise ao código que está nos anexos.
80
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Bibliografia
[1] Serrão, C. et al – Programação com PHP 5.3. 1ª ed. Lisboa: FCA, 2010. ISBN 978-972-722-
341-1
[2] Coelho, P. – JavaScript - Animação e Programação em Páginas Web. 1ª ed. Lisboa: FCA,
2003. ISBN 972-722-254-4
[3] Pereira, J. – Tecnologia de Base de Dados. 1ª ed. Lisboa: FCA, 2002. ISBN 972-722-143-2
[4] Oliveira, H. – Fundamental do Dreamweaver MX 2004. 1ª ed. Lisboa: FCA, 2003. ISBN 972-
722-439-3
[5] Ramalho, J. et al – XML & XSL. 1ª ed. Lisboa: FCA, 2002. ISBN 972-722-347-8
[6] Google Code et al – Referência da API do Google Maps: Google Code, 2010.
[7] Swedberg, K. et al – Referência da API jQuery: jQuery Team, 2010.
[8] Bakken, S. et al – PHP Manual. 8ª ed. Hannover: PHP Documentation Group, 2004.
[9] Axmark, D. et al – MySQL Reference Manual. 4ª ed. Estocolmo: MySQL Documentation
Team, 2004.
81
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Anexos
Referência de funções da API do Google Maps
Funções para exibição de conteúdo GMap2 GMapOptions GGoogleBarOptions
GInfoWindow GInfoWindowTab GInfoWindowOptions
GMarker GMarkerOptions GPolvline
GPolvlineOptions GPolvEditingOptions GPolvStvleOptions
GPolvgon GPolvgonOptions GScreenOverlav
GScreenPoint GScreenSize GGroundOverlav
GIcon GPoint GSize
GBounds GLatLng GLatLngBounds
GControl GTileLaverOptions GTileLaverOverlavOptions
GEvent GEventListener GXmlHttp
GXml GXslt GLog
GDraggableObject GDraggableObjectOptions GGeoStatusCode
GGeoAddressAccuracv GClientGeocoder GGeocodeCache
GFactualGeocodeCache GMarkerManager GMarkerManagerOptions
GGeoXml GDownloadUrl GBrowserIsCompatible
GDirections GDirectionsOptions GTravelModes
GRoute GStep GTrafficOverlav
GTrafficOverlavOptions GAdsManager GAdsManagerOptions
GStreetviewPanorama GStreetviewPanoramaOptions GStreetviewOverlav
GStreetviewClient GStreetviewClient.
ReturnValues
GStreetviewData
GStreetviewLocation GStreetviewLink GPov
GStreetviewPanorama.
ErrorViewer
82
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Funções para extensão de funcionalidades Funções para estender as funcionalidades da API do Google Maps por meio da implementação
dos nossos próprios controlos, sobreposições ou tipos personalizados de mapas:
GGoogleBarListingTvpes
GGoogleBarLinkTarget
GGoogleBarResultList
GMapPane
GOverlav
GControl
GControlPosition
GControlAnchor
GMapTvpeControl
GMenuMapTvpeControl
GHierarchicalMapTvpeControl
GMapTvpe
GMapTvpeOptions
GLaver
GTileLaver GTileLaverOverlav
GCopvrightCollection
GCopvright
GProjection
GMercatorProjection
83
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
Scr ipt AjaxCRUD Código PHP e jQuery/JavaScript da classe que permite gerar interfaces de gestão de base de
dados AJAX de uma forma expedita.
Esta classe contém os comentários em Inglês pois foi usqada para concorrer a um concurso no
site Envato CodeCanyon. Os comentários explicam bem o funcionamento dos scripts.
ajaxCRUD.class.php <?php /******************************************************/ /* ajaxCRUD.class.php v2.1 */ /* =========================== */ /* 2009 José Alves – [email protected] */ /* best PHP/jQuery class contest */ /****************************************************/ define('EXECUTING_SCRIPT', $_SERVER['PHP_SELF']); #this top part is for the AJAX actions themselves. the class is BELOW $ajaxAction = $_REQUEST['ajaxAction']; if ($ajaxAction != ""){ //these lines make sure caching do not cause AJAX saving/displaying issues header("Cache-Control: no-cache, must-revalidate"); //A date in the past header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); $table = $_REQUEST['table']; $pk = $_REQUEST['pk']; $field = trim($_REQUEST['field']); $id = $_REQUEST['id']; $val = $_REQUEST['val']; $table_num = $_REQUEST['table_num']; if (!is_numeric($id)){ $sql_id = "\"$id\""; } else{ $sql_id = $id; } if ($ajaxAction == 'add'){ echo $_SESSION[$table]; } if ($ajaxAction == 'filter'){ echo $_SESSION[$table]; } if ($ajaxAction == 'sort'){ echo $_SESSION[$table];
84
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} if ($ajaxAction == 'getRowCount'){ echo $_SESSION['row_count']; } if ($ajaxAction == 'update'){ $val = str_replace("\n","<br />", $val); //$val = str_replace("<p>","<br /><br />", $val); //$val = str_replace("</P>","", $val); //$val = str_replace("</p>","", $val); //check to see if record exists $row_current_value = q1("SELECT $pk FROM $table WHERE $pk = $sql_id"); if ($row_current_value == ''){ qr("INSERT INTO $table ($pk) VALUES (\"$id\")"); } $success = qr("UPDATE $table SET $field = \"$val\" WHERE $pk = $sql_id"); if ($val == '') $val = " "; //when updating, we use the Table name, Field name, & the Primary Key (id) to feed back to client-side-processing $prefield = trim($table . $field . $id); if (isset($_REQUEST['dropdown_tbl'])){ $val = "{selectbox}"; } if ($success){ echo $prefield . "|" . stripslashes($val); } else{ echo "error|" . $prefield . "|" . stripslashes($val); } } if ($ajaxAction == 'delete'){ qr("DELETE FROM $table WHERE $pk = $sql_id"); echo $table . "|" . $id; } exit(); } // THE AJAXCRUD CLASS FOLLOWS: // Use: // Create an ajaxCRUD object. // $table = new ajaxCRUD(name of item, table name, primary key); // Example: // $tblFAQ = new ajaxCRUD("FAQ", "tblFAQ", "pkFAQID"); // $tblFAQ->showTable(); //
85
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
// Note: !! Your table must have AUTO_INCREMENT enabled for the primary key !! // Note: !! Your version of mySQL must support string->INT conversion (thus "1" = 1) and "" is a NULL value !! class ajaxCRUD{ var $ajaxcrud_root; var $AJAX_file; var $css_file; var $css = true; //indicates a css spredsheet WILL be used var $add = true; //adding is ok var $doActionOnShowTable; //boolean var. When true and showTable() is called, doAction() is also called. turn off when you want to only have a table show in certain conditions but CRUD operations can take place on the table "behind the scenes" var $item_plural; var $item; var $db_table; var $db_table_pk; var $db_main_field; var $row_count; var $table_html; //the html for the table (to be modified on ADD via AJAX) var $cellspacing; var $showPaging = true; var $limit; // limit of rows to display on one page. defaults to 50 var $sql_limit; var $filtered_table = false; //the table is by default unfiltered (eg no 'where clause' on it) var $ajaxFilter_fields = array(); //array of fields that can be are filtered by AJAX (creates a textbox at the top of the table) var $ajaxFilterBoxSize = array(); //array (sub fieldname) holding size of the input box //all fields in the table var $fields = array(); var $field_count; //field datatypes var $field_datatype = array(); //$field_datatype[field] = datatype //allow delete of fields | boolean variable set to true by default var $delete; //defines if the add function uses AJAX var $AJAX_add = true; //defines if the class allows you to edit all fields var $AJAX_editing = true; //the fields to be displayed var $display_fields = array();
86
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//the fields to be inputted when adding a new entry (90% time will be all fields). can be changed via the omitAddField method var $add_fields = array(); var $add_form_top = FALSE; //the add form (by default) is below the table. use displayAddFormTop() to bring it to the top //the fields which are displayed, but not editable var $uneditable_fields = array(); var $sql_where_clause; var $sql_where_clauses = array(); //array for IF there is more than one where clause var $sql_order_by; var $num_where_clauses; var $on_add_specify_primary_key = false; //table border - default is off: 0 var $border; //array containing values for a button next to the "go back" button at the bottom. [0] = value [1] = url [2] = extra tags/JavaScript var $bottom_button = array(); //array with value being the url for the buttom to go to (passing the id) [0] = value [1] = url var $row_button = array(); ################################################ # # The following are parallel arrays to help in the definition of a defined db relationship # ################################################ //values will be the name(s) of the foreign key(s) for a category table var $db_table_fk_array = array(); //values will be the name(s) of the category table(s) var $category_table_array = array(); //values will be the name(s) of the primary key for the category table(s) var $category_table_pk_array = array(); //values will be the name(s) of the field to return in the category table(s) var $category_field_array = array(); //values will be the (optional) name of the field to sort by in the category table(s) var $category_sort_field_array = array(); //for dropdown (to make an empty box). (format: array[field] = true/false) var $category_required = array(); // allowable values for a field. the key is the name of the field
87
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var $allowed_values = array(); // "on" and "off" values for a checkbox. the key is the name of the field var $checkbox = array(); // holds the field names of columns that will have a "check all" checkbox var $checkboxall = array(); //values to be set to a particular field when a new row is added. the array is set as $field_name => $add_value var $add_values = array(); //destination folder to be set for a particular field that allows uploading of files. the array is set as $field_name => $destination_folder var $file_uploads = array(); var $file_upload_info = array(); //array[$field_name][destination_folder] and array[$field_name][relative_folder] var $filename_append_field = ""; //array dictating that "dropdown" fields do not show dropdown (but text editor) on edit (format: array[field] = true/false); //used in defineAllowableValues function var $field_no_dropdown = array(); //array holding the (user-defined) function to format a field with on display (format: array[field] = function_name); //used in formatFieldWithFunction function var $format_field_with_function = array(); var $onAddExecuteCallBackFunction; //(if true) put a checkbox before each row var $showCheckbox; var $loading_image_html; var $sort_direction; //used when sorting the table via AJAX ################################################ # # displayAs array is for linking a particular field to the name that displays for that field # ################################################ //the indexes will be the name of the field. the value is the displayed text var $displayAs_array = array(); //height of the textarea for certain fields. the index is the field and the value is the height var $textarea_height = array(); //campos password var $set_password = array();
88
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//campos data var $set_data = array(); // Constructor //by default ajaxCRUD assumes all necessary files are in the same dir as the script calling it (eg $ajaxcrud_root = "") function ajaxCRUD($item, $db_table, $db_table_pk, $ajaxcrud_root = "") { //global variable - for allowing multiple ajaxCRUD tables on one page global $num_ajaxCRUD_tables_instantiated; if ($num_ajaxCRUD_tables_instantiated === "") $num_ajaxCRUD_tables_instantiated = 0; $this->showCheckbox = false; $this->ajaxcrud_root = $ajaxcrud_root; //$this->AJAX_file = "AJAX_ajaxCRUD.php"; $this->AJAX_file = EXECUTING_SCRIPT; $this->item = $item; $this->item_plural = $item . "s"; $this->db_table = $db_table; $this->db_table_pk = $db_table_pk; $this->fields = $this->getFields($db_table); $this->field_count = count($this->fields); //by default paging is turned on; limit is 50 $this->showPaging = true; $this->limit = 50; $this->num_where_clauses = 0; $this->delete = true; $this->add = true; //assumes the primary key is auto incrementing $this->primaryKeyAutoIncrement = true; $this->border = 0; $this->css = true; $this->AJAX_add = true; $this->doActionOnShowTable = true; $this->loading_image_html = "<center><br /><br /><img src=\'" . $this->ajaxcrud_root . "css/loading.gif\'><br /><br /></center>"; //changed via setLoadingImageHTML() $this->onAddExecuteCallBackFunction = ''; $this->onFileUploadExecuteCallBackFunction = ''; $this->onDeleteFileExecuteCallBackFunction = ''; //don't allow primary key to be editable $this->uneditable_fields[] = $this->db_table_pk; $this->display_fields = $this->fields; $this->add_fields = $this->fields;
89
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//default sort direction $this->sort_direction = "desc"; if ($this->field_count == 0){ $error_msg[] = "No fields in this table!"; echo_msg_box(); exit(); } //for filtering if there is a request parameter $count_filtered = 0; $action = $_REQUEST['action']; foreach ($this->fields as $field){ if ($_REQUEST[$field] != '' && ($action != 'add' && $action != 'delete' && $action != 'update' && $action != 'upload' && $action != 'delete_file')){ $filter_field = $field; $filter_value = $_REQUEST[$field]; $filter_where_clause = "WHERE $filter_field LIKE \"%" . $filter_value . "%\""; $this->addWhereClause($filter_where_clause); $this->filtered_table = true; $count_filtered++; } } if ($count_filtered > 0){ $this->filtered_table; } else{ $this->filtered_table = false; } return true; } function setAjaxFile($AJAX_file){ $this->AJAX_file = $AJAX_file; } function turnOffAjaxADD(){ $this->AJAX_add = false; } function turnOffAjaxEditing(){ $this->AJAX_editing = false; } function turnOffPaging($limit = ""){ $this->showPaging = false; if ($limit != ''){ $this->sql_limit = " LIMIT $limit"; } } function setCSSFile($css_file){ $this->css_file = $css_file; } function setLoadingImageHTML($html){ $this->loading_image_html = $html;
90
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} function addTableBorder(){ $this->border = 1; } function addAjaxFilterBox($field_name){ $this->ajaxFilter_fields[] = $field_name; $this->setAjaxFilterBoxSize($field_name, 10); } function setAjaxFilterBoxSize($field_name, $size){ $this->ajaxFilterBoxSize[$field_name] = $size; } function addAjaxFilterBoxAllFields(){ //unset($this->ajaxFilter_fields); foreach ($this->display_fields as $field){ $this->addAjaxFilterBox($field); } } function displayAddFormTop(){ $this->add_form_top = TRUE; } function addWhereClause($sql_where_clause){ $this->num_where_clauses++; $this->sql_where_clauses[] = $sql_where_clause; if ($this->num_where_clauses <= 1){ $this->sql_where_clause = " " . $sql_where_clause; } else{ foreach($this->sql_where_clauses as $where_clause){ $new_where = str_replace("WHERE", "AND", $where_clause); $this->sql_where_clause .= " $new_where "; } } } function addOrderBy($sql_order_by){ $this->sql_order_by = " " . $sql_order_by; } function formatFieldWithFunction($field, $function_name){ $this->format_field_with_function[$field] = $function_name; } function defineRelationship($fkCategoryID, $category_table, $category_table_pk, $category_field_name, $category_sort_field = "", $category_required = "1"){ $this->db_table_fk_array[] = $fkCategoryID; $this->category_table_array[] = $category_table; $this->category_table_pk_array[] = $category_table_pk; $this->category_field_array[] = $category_field_name; $this->category_sort_field_array[] = $category_sort_field;
91
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//make the relationship required for the field if ($category_required == "1"){ $this->category_required[$fkCategoryID] = TRUE; } } function relationshipFieldOptional(){ $this->cat_field_required = FALSE; } function defineAllowableValues($field, $array_values, $onedit_textbox = FALSE){ //array with the setup [0] = value [1] = display name (both the same) $new_array = array(); foreach($array_values as $array_value){ if (!is_array($array_value)){ //a two-dimentential array --> set both the value and dropdown text to be the same $new_array[] = array(0=> $array_value, 1=>$array_value); } else{ //a 2-dimentential array --> value and dropdown text are different $new_array[] = $array_value; } } if ($onedit_textbox != FALSE){ $this->field_no_dropdowkn[$field] = TRUE; } $this->allowed_values[$field] = $new_array; } function defineCheckbox($field, $value_on="1", $value_off="0"){ $new_array = array($value_on, $value_off); $this->checkbox[$field] = $new_array; } function showCheckboxAll($field, $display_data) { $this->checkboxall[$field] = $display_data; } function displayAs($field, $the_field_name){ $this->displayAs_array[$field] = $the_field_name; } function setTextareaHeight($field, $height){ $this->textarea_height[$field] = $height; } function setPassword($field){ $this->set_password[$field] = 1; } function setData($field){
92
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$this->set_data[$field] = 1; } function setLimit($limit){ $this->limit = $limit; } function getRowCount(){ if ($_SESSION['row_count'] == ""){ $count = q1("SELECT COUNT(*) FROM " . $this->db_table . $this->sql_where_clause . $this->sql_order_by); } else{ $count = $_SESSION['row_count']; } return "<span id='" . $this->db_table . "_RowCount'>" . $count . "</span>"; } function getTotalRowCount(){ $count = q1("SELECT COUNT(*) FROM " . $this->db_table); return $count; } function omitField($field_name){ $key = array_search($field_name, $this->display_fields); if ($this->fieldInArray($field_name, $this->display_fields)){ unset($this->display_fields[$key]); } else{ $error_msg[] = "Error in your doNotDisplay function call. There is no field named <b>$field_name</b> in the table <b>" . $this->db_table . "</b>"; } } function omitAddField($field_name){ $key = array_search($field_name, $this->add_fields); if ($key !== FALSE){ unset($this->add_fields[$key]); } else{ $error_msg[] = "Error in your omitAddField function call. There is no field named <b>$field_name</b> in the table <b>" . $this->db_table . "</b>"; } } function omitFieldCompletely($field_name){ $this->omitField($field_name); $this->omitAddField($field_name); } function addValueOnInsert($field_name, $insert_value){ //if the value is NOW() then it's a date field or if its an integer --> otherwise put quotes around it. //if ($insert_value != "NOW()" && !is_int($insert_value)){ // $insert_value = "\"" . $insert_value . "\""; //}
93
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$this->add_values[] = array(0 => $field_name, 1 => $insert_value); } function onAddExecuteCallBackFunction($function_name){ $this->onAddExecuteCallBackFunction = $function_name; $this->AJAX_add = false; } function onFileUploadExecuteCallBackFunction($function_name){ $this->onFileUploadExecuteCallBackFunction = $function_name; } function onDeleteFileExecuteCallBackFunction($function_name){ $this->onDeleteFileExecuteCallBackFunction = $function_name; } function primaryKeyNotAutoIncrement(){ $this->primaryKeyAutoIncrement = false; } function setFileUpload($field_name, $destination_folder, $relative_folder = ""){ //put values into array $this->file_uploads[] = $field_name; $this->file_upload_info[$field_name][destination_folder] = $destination_folder; $this->file_upload_info[$field_name][relative_folder] = $relative_folder; //the filenames that are saved are not editable $this->disallowEdit($field_name); //have to add the row via POST now $this->AJAX_add = false; } function appendUploadFilename($append_field){ $this->filename_append_field = $append_field; } function omitPrimaryKey(){ //99% time it'll be in key 0, but just in case do search $key = array_search($this->db_table_pk, $this->display_fields); unset($this->display_fields[$key]); } function insertHeader($AJAX_file){ if ($this->css_file == ''){ $this->css_file6 = 'default.css'; } /* Load JavaScript dependencies */ echo "<script type=\"text/JavaScript\" src=\"http://AJAX.googleapis.com/AJAX/libs/jQuery/1.3.2/jQuery.min.js\"></script>\n"; //per release 8, using jQuery instead of protoculous
94
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
echo "<script src=\"" . $this->ajaxcrud_root . "JavaScript_functions.js\" type=\"text/JavaScript\"></script>\n"; echo "<link href=\"" . $this->ajaxcrud_root . "css/ajaxcrud.css\" rel=\"stylesheet\" type=\"text/css\" media=\"screen\" />\n"; echo "<link href=\"" . $this->ajaxcrud_root . "css/datePicker.css\" rel=\"stylesheet\" type=\"text/css\" media=\"screen\" />\n"; echo "<script type=\"text/JavaScript\" src=\"" . $this->ajaxcrud_root . "date.js\"></script>\n"; echo "<script type=\"text/JavaScript\" src=\"" . $this->ajaxcrud_root . "jQuery.datePicker.js\"></script>\n"; echo "<!--[if IE]><script type=\"text/JavaScript\" src=\"" . $this->ajaxcrud_root . "jQuery.bgiframe.min.js\"></script><![endif]-->\n"; echo "<script type=\"text/JavaScript\" charset=\"utf-8\">\n Date.format = 'yyyy-mm-dd';\n $(function()\n {\n $('.date-pick').datePicker({clickInput:true})\n });\n </script>\n" ; echo " <script>\n AJAX_file = \"$this->AJAX_file\"; \n this_page = \"" . $_SERVER['PHP_SELF'] . "\"; \n loading_image_html = \"$this->loading_image_html\"; \n </script>\n"; //are we even using a stylesheet? (it can be turned off) if ($this->css){ echo "<link href=\"" . $this->ajaxcrud_root . "css/" . $this->css_file ."\" rel=\"stylesheet\" type=\"text/css\" media=\"screen\" />\n"; } return true; } function disallowEdit($field){ $this->uneditable_fields[] = $field; } function disallowDelete(){ $this->delete = false; } function disallowAdd(){ $this->add = false; } function addButton($value, $url, $tags = ""){ $this->bottom_button = array(0 => $value, 1 => $url, 2 => $tags); } function addButtonToRow($value, $url, $attach_params = "", $JavaScript_tags = ""){
95
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$this->row_button[] = array(0 => $value, 1 => $url, 2 => $attach_params, 3 => $JavaScript_tags); } function onAddSpecifyPrimaryKey(){ $this->on_add_specify_primary_key = true; } function doCRUDAction(){ if ($_REQUEST['action'] != ''){ $this->doAction($_REQUEST['action']); } } function doAction($action){ global $error_msg; global $report_msg; $item = $this->item; if ($action == 'delete' && $_REQUEST['id'] != ''){ $delete_id = $_REQUEST['id']; $success = qr("DELETE FROM $this->db_table WHERE $this->db_table_pk = \"$delete_id\""); if ($success){ $report_msg[] = "$item Apagado"; } else{ $error_msg[] = "$item não pôde ser apagado. Por favor tente outra vez."; } }//action = delete #adding new item (via traditional way, non-AJAX -- note: this is the ONLY way files can be uploaded with ajaxCRUD) if ($actikon == 'add'){ //this if condition is so MULTIPLE ajaxCRUD tables can be used on the same page. if ($_REQUEST['table'] == $this->db_table){ //for sql insert statement $submitted_values = array(); //for callback function (if defined) $submitted_array = array(); //this new row has (a) file(s) coming with it $uploads_on = $_REQUEST['uploads_on']; if ($uploads_on == 'true' && $_FILES){ $uploads_on = true; } foreach($this->fields as $field){ $submitted_value_cleansed = ""; if ($_REQUEST[$field] == ''){ if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){
96
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$submitted_value_cleansed = 0; } } else{ $submitted_value_cleansed = $_REQUEST[$field]; } $submitted_values[] = $submitted_value_cleansed; //also used for callback function $submitted_array[$field] = $submitted_value_cleansed; } //get rid of the primary key in the fields column if (!$this->on_add_specify_primary_key){ unset($submitted_values[0]); //assumes the primary key is the FIRST field in the array } //for adding values to the row which were not in the ADD row table - but are specified by ADD on INSERT if (count($this->add_values) > 0){ foreach ($this->add_values as $add_value){ $field_name = $add_value[0]; $the_add_value = $add_value[1]; if ($submitted_array[$field_name] == ''){ $submitted_array[$field_name] = $the_add_value; } //reshuffle numeric indexed array unset($submitted_values); $submitted_values = array(); foreach($submitted_array as $field){ $submitted_values[] = $field; } //get rid of the primary key in the fields column if (!$this->on_add_specify_primary_key){ unset($submitted_values[0]); //assumes the primary key is the FIRST field in the array } } } //wrap each field in quotes $string_submitted_values = "\"" . implode("\",\"", $submitted_values) . "\"";; //for getting datestamp of new row for mysql's "NOW" to work $string_submitted_values = str_replace('"NOW()"', 'NOW()', $string_submitted_values); //print_r($submitted_values);
97
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
if ($string_submitted_values != ''){ if (!$this->on_add_specify_primary_key && $this->primaryKeyAutoIncrement){ //don't allow the primary key to be inputted $fields_array_without_pk = $this->fields; unset($fields_array_without_pk[0]); //assumes the primary key is the FIRST field in the array $string_fields_without_pk = implode(",", $fields_array_without_pk); $query = "INSERT INTO $this->db_table($string_fields_without_pk) VALUES ($string_submitted_values)"; } else{ if (!$this->primaryKeyAutoIncrement){ $primary_key_value = q1("SELECT MAX($this->db_table_pk) FROM $this->db_table"); if ($primary_key_value > 0) $primary_key_value++; $primary_key_value = $primary_key_value . ", "; } $string_fields_with_pk = implode(",", $this->fields); $query = "INSERT INTO $this->db_table($string_fields_with_pk) VALUES ($primary_key_value $string_submitted_values)"; } $success = qr($query); if ($success){ $insert_id = mysql_insert_id(); //$_SESSION[insert_id] = $insert_id; $report_msg[] = "$item Adicionado"; if ($uploads_on){ foreach($this->file_uploads as $field_name){ $file_dest = $this->file_upload_info[$field_name][destination_folder]; if ($_FILES[$field_name]['name'] != ''){ $this->uploadFile($insert_id, $field_name, $file_dest); } } } if ($this->onAddExecuteCallBackFunction != ''){ $submitted_array[id] = $insert_id; $submitted_array[$this->db_table_pk] = $insert_id; call_user_func($this->onAddExecuteCallBackFunction, $submitted_array); }
98
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} else{ $error_msg[] = "$item nao pode ser adicionado. Por favor tente outra vez."; } } else{ $error_msg[] = "Todos os campos foram omitidos."; } }//if POST parameter 'table' == db_table }//action = add if ($action == 'upload' && $_REQUEST['field_name'] && $_REQUEST['id'] != ''){ $update_id = $_REQUEST['id']; $file_field = $_REQUEST['field_name']; $upload_folder = $this->file_upload_info[$file_field][destination_folder]; $success = $this->uploadFile($update_id, $file_field, $upload_folder); if ($success){ $report_msg[] = "Ficheiro carregado com sucesso."; } else{ $error_msg[] = "Houve um erro ao carregar o ficheiro (ou nenhum ficheiro foi seleccionado)."; } }//action = upload if ($action == 'delete_file' && $_REQUEST['field_name'] && $_REQUEST['id'] != ''){ $delete_id = $_REQUEST['id']; $file_field = $_REQUEST['field_name']; $upload_folder = $_REQUEST['upload_folder']; $filename = q1("SELECT $file_field FROM $this->db_table WHERE $this->db_table_pk = $delete_id"); $success = qr("UPDATE $this->db_table SET $file_field = \"\" WHERE $this->db_table_pk = $delete_id"); if ($success){ $file_dest = $this->file_upload_info[$file_field][destination_folder]; unlink($file_dest . $filename); $report_msg[] = "Ficheiro apagado com sucesso."; if ($this->onDeleteFileExecuteCallBackFunction != ''){ $delete_file_array = array(); $delete_file_array[id] = $delete_id; $delete_file_array[field] = $file_field; call_user_func($this->onDeleteFileExecuteCallBackFunction, $delete_file_array); } } else{
99
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$error_msg[] = "Houve um erro ao apagar o ficheiro."; } }//action = delete_file }//doAction //a file must have been "sent"/posted for this to work function uploadFile($row_id, $file_field, $upload_folder){ @$fileName = $_FILES[$file_field]['name']; @$tmpName = $_FILES[$file_field]['tmp_name']; @$fileSize = $_FILES[$file_field]['size']; @$fileType = $_FILES[$file_field]['type']; $new_filename = make_filename_safe($fileName); if ($this->filename_append_field != ""){ if ($_REQUEST[$this->filename_append_field] != ''){ $new_filename = $_REQUEST[$this->filename_append_field] . "_" . $new_filename; } else{ if ($this->filename_append_field == $this->db_table_pk){ $new_filename = $row_id . "_" . $new_filename; } else{ @$db_value_to_append = q1("SELECT $this->filename_append_field FROM $this->db_table WHERE $this->db_table_pk = $row_id"); if ($db_value_to_append != ""){ $new_filename = $db_value_to_append . "_" . $new_filename; } } } } $destination = $upload_folder . $new_filename; $success = move_uploaded_file ($tmpName, $destination); if ($success){ $update_success = qr("UPDATE $this->db_table SET $file_field = \"$new_filename\" WHERE $this->db_table_pk = $row_id"); if ($this->onFileUploadExecuteCallBackFunction != ''){ $file_info_array = array(); $file_info_array[id] = $row_id; $file_info_array[field] = $file_field; $file_info_array[fileName] = $new_filename; $file_info_array[fileSize] = $fileSize; $file_info_array[fldType] = $fldType; call_user_func($this->onFileUploadExecuteCallBackFunction, $file_info_array); } } if ($update_success){
100
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
return true; //$report_msg[] = "File Uploaded."; } else{ return false; //$error_msg[] = "There was an error uploading your file (or none was selected)."; } } function showTable(){ global $error_msg; global $report_msg; global $warning_msg_displayed; global $num_ajaxCRUD_tables_instantiated; $num_ajaxCRUD_tables_instantiated++; /* Sort Table Note: this cancels out default sorting set by addOrderBy() */ if ($this->db_table == $_REQUEST['table'] && $_REQUEST['sort_field'] != ''){ $sort_field = $_REQUEST['sort_field']; $user_sort_order_direction = $_REQUEST['sort_direction']; if ($user_sort_order_direction == 'asc'){ $this->sort_direction = "desc"; } else{ $this->sort_direction = "asc"; } $sort_sql = " ORDER BY $sort_field $this->sort_direction"; $this->addOrderBy($sort_sql); $this->sorted_table = true; } //the HTML to display $top_html = ""; //top header stuff $table_html = ""; //for the html table itself $bottom_html = ""; $add_html = ""; //for the add form $html = ""; //all combined if ( $num_ajaxCRUD_tables_instantiated == 1 ){ //pull in the css and JavaScript files $this->insertHeader($this->AJAX_file); } if ($this->doActionOnShowTable){ if ($_REQUEST['action'] != ''){ $this->doAction($_REQUEST['action']); } } $item = $this->item;
101
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$top_html .= "<a name='ajaxCRUD" . $num_ajaxCRUD_tables_instantiated ."' id='ajaxCRUD" . $num_ajaxCRUD_tables_instantiated ."'></a>\n"; if (count($this->ajaxFilter_fields) > 0){ $top_html .= "<form id=\"" . $this->db_table . "_filter_form\">\n"; $top_html .= "<table cellspacing='5' align='center'><tr>"; foreach ($this->ajaxFilter_fields as $filter_field){ $display_field = $filter_field; if ($this->displayAs_array[$filter_field] != ''){ $display_field = $this->displayAs_array[$filter_field]; } $textbox_size = $this->ajaxFilterBoxSize[$filter_field]; $filter_value = ""; if ($_REQUEST[$filter_field] != ''){ $filter_value = $_REQUEST[$filter_field]; } $top_html .= "<td>Filtrar por <b>$display_field</b>: <input type=\"text\" size=\"$textbox_size\" name=\"$filter_field\" value=\"$filter_value\" onKeyUp=\"filterTable(this, '" . $this->db_table . "', '$filter_field', '$extra_query_params');\"></td>"; } $top_html .= "</tr></table>\n"; $top_html .= "</form>\n"; } ############################################# # # Begin code for displaying database elements # ############################################# $select_fields = implode(",", $this->fields); $sql = "SELECT * FROM " . $this->db_table . $this->sql_where_clause . $this->sql_order_by; if ($this->showPaging){ $pageid = $_GET['pid'];//Get the pid value if(intval($pageid) == 0) $pageid = 1; $Paging = new paging(); $Paging->tableName = $this->db_table; $total_records = $Paging->myRecordCount($sql);//count records $totalpage = $Paging->processPaging($this->limit,$pageid); $rows = $Paging->startPaging($sql);//get records in the databse $links = $Paging->pageLinks(basename($PHP_SELF));//1234 links unset($Paging); }
102
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
else{ $rows = q($sql . $this->sql_limit); } //$rows = q("SELECT * FROM " . $this->db_table"); $row_count = count($rows); $this->row_count = $row_count; $_SESSION['row_count'] = $row_count; if ($row_count == 0){ $report_msg[] = "Esta tabela nao tem dados."; } #this is an optional function which will allow you to display errors or report messages as desired. comment it out if desired //only show the message box if it hasn't been displayed already if ($warning_msg_displayed == 0 || $warning_msg_displayed == ''){ echo_msg_box(); } $dropdown_array = array(); foreach ($this->category_table_array as $key => $category_table){ $category_field_name = $this->category_field_array[$key]; $category_table_pk = $this->category_table_pk_array[$key]; $order_by = ''; if ($this->category_sort_field_array[$key] != ''){ $order_by = " ORDER BY " . $this->category_sort_field_array[$key]; } $dropdown_array[] = q("SELECT $category_table_pk, $category_field_name FROM $category_table $order_by"); } $top_html .= "<div id='$this->db_table'>\n"; if ($row_count > 0){ //$edit_word = "Edit"; //if ($row_count == 0) $edit_word = "No"; //$top_html .= "<h3>Edit " . $this->item_plural . "</h3>\n"; $table_html .= "<table align='center' class='ajaxCRUD' name='table_" . $this->db_table . "' id='table_" . $this->db_table . "' cellspacing='" . $this->cellspacing . "' border=" . $this->border . ">\n"; $table_html .= "<tr>\n"; //for an (optional) checkbox if ($this->showCheckbox){ $table_html .= "<th> </th>"; }
103
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
foreach ($this->display_fields as $field){ $field_name = $field; if ($this->displayAs_array[$field] != ''){ $field = $this->displayAs_array[$field]; } if (array_key_exists($field_name, $this->checkboxall)) { $table_html .= "<th><input type=\"checkbox\" name=\"$field_name" . "_checkboxall\" value=\"checkAll\" onClick=\" if (this.checked) { setAllCheckboxes('$field_name" . "_fieldckbox',false); } else { setAllCheckboxes('$field_name" . "_fieldckbox',true); } \">"; if ($this->checkboxall[$field_name] == true) { $table_html .= "<a href='JavaScript:;' onClick=\"changeSort('$this->db_table', '$field_name', '$this->sort_direction');\" >" . $field . "</a>"; } $table_html .= "</th>"; } else { $table_html .= "<th><a href='JavaScript:;' onClick=\"changeSort('$this->db_table', '$field_name', '$this->sort_direction');\" >" . $field . "</a></th>"; } } if ($this->delete || (count($this->row_button)) > 0){ $table_html .= "<th>Acções</th>\n"; } $table_html .= "</tr>\n"; $count = 0; $class = "odd"; $attach_params = ""; foreach ($rows as $row){ $id = $row[$this->db_table_pk]; $table_html .= "<tr class='$class' id=\"" . $this->db_table . "_row_$id\" valign='top'>\n"; if ($this->showCheckbox){ $checkbox_selected = ""; if ($id == $_REQUEST[$this->db_table_pk]) $checkbox_selected = " checked"; $table_html .= "<td><input type='checkbox' $checkbox_selected onClick=\"window.location ='" . $_SERVER['PHP_SELF'] . "?$this->db_table_pk=$id'\" /></td>"; } foreach($this->display_fields as $field){ $cell_data = $row[$field];
104
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//for adding a button via addButtonToRow (using "all" as the "attach params" optional third parameter) if (count($this->row_button) > 0){ $attach_params .= "&" . $field . "=" . $cell_data; } $cell_value = $cell_data; if ($this->format_field_with_function[$field] != ''){ $cell_data = call_user_func($this->format_field_with_function[$field], $cell_data); } //try to find a reference to another table relationship $found_category_index = array_search($field, $this->db_table_fk_array); //don't allow uneditable fields (which usually includes the primary key) to be editable if ( ($this->fieldInArray($field, $this->uneditable_fields) || (!$this->AJAX_editing)) && $found_category_index == ''){ $table_html .= "<td>"; $key = array_search($field, $this->display_fields); if ($this->fieldInArray($field, $this->file_uploads)){ //a file exists for this field if ($cell_data != ''){ $file_link = $this->file_upload_info[$field][relative_folder] . $row[$field]; $file_dest = $this->file_upload_info[$field][destination_folder]; $extensao = substr(strtolower($file_link), -3); if( $extensao == "jpg" || $extensao == "jpeg" ||$extensao == "png" || $extensao == "bmp" || $extensao == "gif" ){ $table_html .= "<span id='text_" . $field . $id . "'><img src=\"$file_link\"> <br> (<a style=\"font-size: 9px;\" href=\"JavaScript:\" onClick=\"document.getElementById('file_$field$id').style.display = ''; document.getElementById('text_$field$id').style.display = 'none'; \">editar</a> <a style=\"font-size: 9px;\" href=\"JavaScript:\" onClick=\"deleteFile('$field', '$id')\">apagar</a>)</span> \n"; } else{ $table_html .= "<span id='text_" . $field . $id . "'><a href=\"$file_link\" target=_blank >Ver Ficheiro</a> <br> (<a style=\"font-size: 9px;\" href=\"JavaScript:\" onClick=\"document.getElementById('file_$field$id').style.display =
105
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
''; document.getElementById('text_$field$id').style.display = 'none'; \">editar</a> <a style=\"font-size: 9px;\" href=\"JavaScript:\" onClick=\"deleteFile('$field', '$id')\">apagar</a>)</span> \n"; } $table_html .= "<div id='file_" . $field . $id . "' style='display:none;'>\n"; $table_html .= $this->showUploadForm($field, $file_dest, $id); $table_html .= "</div>\n"; } if ($cell_data == ''){ $table_html .= "<span id='text_" . $field . $id . "'><a style=\"font-size: 9px;\" href=\"JavaScript:\" onClick=\"document.getElementById('file_$field$id').style.display = ''; document.getElementById('text_$field$id').style.display = 'none'; \">Adicionar Ficheiro</a></span> \n"; $table_html .= "<div id='file_" . $field. $id . "' style='display:none;'>\n"; $table_html .= $this->showUploadForm($field, $file_dest, $id); $table_html .= "</div>\n"; } } else{ $table_html .= $cell_data; } }//if field is not editable else{ $table_html .= "<td>"; if (!is_numeric($found_category_index) && $found_category_index == ''){ //was allowable values for this field defined? if (is_array($this->allowed_values[$field]) && !$this->field_no_dropdown[$field]){ $table_html .= $this->makeAjaxDropdown($id, $field, $cell_data, $this->db_table, $this->db_table_pk, $this->allowed_values[$field]); } else{ //if a checkbox if (is_array($this->checkbox[$field])){ $table_html .= $this->makeAjaxCheckbox($id, $field, $cell_data); } else{ //is an editable field
106
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
if ($cell_data == '') $cell_data = " "; $field_onKeyPress = ""; if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){ $field_onKeyPress = "return fn_validateNumeric(event, this, 'n');"; if ($this->fieldIsDecimal($this->getFieldDataType($field))){ $field_onKeyPress = "return fn_validateNumeric(event, this, 'y');"; } } if ($this->fieldIsEnum($this->getFieldDataType($field))){ $allowed_enum_values_array = $this->getEnumArray($this->getFieldDataType($field)); $table_html .= $this->makeAjaxDropdown($id, $field, $cell_data, $this->db_table, $this->db_table_pk, $allowed_enum_values_array); } else{ $field_length = strlen($row[$field]); // date-calendar if ( $this->set_data[$field] == 1 ){ $table_html .= $this->makeAjaxEditor($id, $field, $cell_value, 'data', $field_length, $cell_data, $field_onKeyPress); } // texto normal else if ($this->set_data[$field] != 1 && $field_length < 51){ $table_html .= $this->makeAjaxEditor($id, $field, $cell_value, 'text', $field_length, $cell_data, $field_onKeyPress); } else{ $textarea_height = ''; if ($this->textarea_height[$field] != '') $textarea_height = $this->textarea_height[$field]; $table_html .= $this->makeAjaxEditor($id, $field, $cell_value, 'textarea', $textarea_height, $cell_data, $field_onKeyPress); } } } } } else{
107
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//this field is a reference to another table's primary key (eg it must be a foreign key) $category_field_name = $this->category_field_array[$found_category_index]; $category_table_name = $this->category_table_array[$found_category_index]; $category_table_pk = $this->category_table_pk_array[$found_category_index]; $selected_dropdown_text = " "; if ($cell_data != ""){ $selected_dropdown_text = q1("SELECT $category_field_name FROM $category_table_name WHERE $category_table_pk = \"" . $cell_data . "\""); if ($selected_dropdown_text == "") $selected_dropdown_text = "--"; } if (!$this->fieldInArray($field, $this->uneditable_fields)){ $table_html .= $this->makeAjaxDropdown($id, $field, $cell_data, $category_table_name, $category_table_pk, $dropdown_array[$found_category_index], $selected_dropdown_text); } else{ $table_html .= $selected_dropdown_text; } } } $html .= "</td>"; } if ($this->delete || (count($this->row_button)) > 0){ $table_html .= "<td>\n"; if ($this->delete){ $table_html .= "<input type=\"button\" class=\"editingSize\" onClick=\"confirmDelete('$id', '" . $this->db_table . "', '" . $this->db_table_pk ."');\" value=\"Apagar\" />\n"; } if (count($this->row_button) > 0){ foreach ($this->row_button as $the_row_button){ $value = $the_row_button[0]; $url = $the_row_button[1]; $attach_param = $the_row_button[2]; $JavaScript_onclick_function = $the_row_button[3]; if ($attach_param == "all"){ $attach = "?attachments" . $attach_params; } else{ $attach = "?" . $this->db_table_pk . "=$id"; }
108
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//its most likely a user-defined AJAX function if ($JavaScript_onclick_function != ""){ $JavaScript_for_button = "onClick=\"" . $JavaScript_onclick_function . "($id);\""; } else{ $JavaScript_for_button = "onClick=\"location.href='" . $url . $attach . "'\""; } $table_html .= "<input type=\"button\" $JavaScript_for_button class=\"btn editingSize\" value=\"$value\" />\n"; } } $table_html .= "</td>\n"; } $table_html .= "</tr>"; if($count%2==0){ $class="cell_row"; } else{ $class="odd"; } $count++; }//foreach row $table_html .= "</table>\n"; //paging links if ($totalpage > 1){ $table_html .= "<br /><div style='width: 800px; position: relative; left: 50%; margin-left: -400px; text-align: center;'><center> $links </center></div><br /><br />"; } }//if rows > 0 //closing div for paging links (if applicable) $bottom_html = "</div><br />\n"; //now we come to the "add" fields if ($this->add){ $add_html .= "<center>\n"; $add_html .= " <input type=\"button\" value=\"Adicionar $item\" class=\"btn editingSize\" onClick=\"$('#add_form_$this->db_table').slideDown('slow');\">\n"; if (count($this->bottom_button) > 0){ $button_value = $this->bottom_button[0]; $button_url = $this->bottom_button[1]; $button_tags = $this->bottom_button[2];
109
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
if ($button_tags == ''){ $tag_stuff = "onClick=\"location.href = '$button_url';\""; } else{ $tag_stuff = $button_tags; } $add_html .= " <input type=\"button\" value=\"$button_value\" href=\"$button_url\" class=\"btn\" $tag_stuff>\n"; } //$add_html .= " <input type=\"button\" value=\"Go Back\" class=\"btn\" onClick=\"history.back();\">\n"; $add_html .= "</center>\n"; $add_html .= "<form action=\"" . $_SERVER['PHP_SELF'] ."#ajaxCRUD\" id=\"add_form_$this->db_table\" method=\"POST\" ENCTYPE=\"multipart/form-data\" style=\"display:none;\">\n"; $add_html .= " <br /><h3>Novo(a) <b>$item</b></h3>\n"; $add_html .= " <table align='center' name='form'>\n"; $add_html .= "<tr valign='top'>\n"; //for here display ALL 'addable' fields foreach($this->add_fields as $field){ if ($field != $this->db_table_pk || $this->on_add_specify_primary_key){ $field_value = ""; if ($_REQUEST[$field] != '') $field_value = $_REQUEST[$field]; if ($this->displayAs_array[$field] != ''){ $display_field = $this->displayAs_array[$field]; } else{ $display_field = $field; } //if a checkbox if (is_array($this->checkbox[$field])){ $values = $this->checkbox[$field]; $value_on = $values[0]; $value_off = $values[1]; $add_html .= "<th width='20%'>$display_field</th><td>\n"; $add_html .= "<input type='checkbox' name=\"$field\" value=\"$value_on\">\n"; $add_html .= "</td></tr>\n"; } else{ $found_category_index = array_search($field, $this->db_table_fk_array); if (!is_numeric($found_category_index) && $found_category_index == ''){ //it's from a set of predefined allowed values for this field if (is_array($this->allowed_values[$field])){
110
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$add_html .= "<th width='20%'>$display_field</th><td>\n"; $add_html .= "<select name=\"$field\" class='editingSize'>\n"; foreach ($this->allowed_values[$field] as $dropdown){ $selected = ""; $dropdown_value = $dropdown[0]; $dropdown_text = $dropdown[1]; if ($field_value == $dropdown_value) $selected = " selected"; $add_html .= "<option value=\"$dropdown_value\" $selected>$dropdown_text</option>\n"; } $add_html .= "</select></td></tr>\n"; } else{ if ($this->fieldInArray($field, $this->file_uploads)){ //this field is an file upload $add_html .= "<th width='20%'>$display_field</th><td><input class=\"editingSize\" type=\"file\" name=\"$field\" size=\"15\"></td></tr>\n"; $file_uploads = true; } else{ if ($this->fieldIsEnum($this->getFieldDataType($field))){ $allowed_enum_values_array = $this->getEnumArray($this->getFieldDataType($field)); $add_html .= "<th width='20%'>$display_field</th><td>\n"; $add_html .= "<select name=\"$field\" class='editingSize'>\n"; foreach ($allowed_enum_values_array as $dropdown){ $dropdown_value = $dropdown; $dropdown_text = $dropdown; if ($field_value == $dropdown_value) $selected = " selected"; $add_html .= "<option value=\"$dropdown_value\" $selected>$dropdown_text</option>\n"; } $add_html .= "</select></td></tr>\n"; }//if enum field else{ $field_onKeyPress = ""; if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){ $field_onKeyPress = "return fn_validateNumeric(event, this, 'n');"; if ($this->fieldIsDecimal($this->getFieldDataType($field))){ $field_onKeyPress = "return fn_validateNumeric(event, this, 'y');"; }
111
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} //textarea fields else if ($this->textarea_height[$field] != ''){ $add_html .= "<th width='20%'>$display_field</th><td><textarea onKeyPress=\"$field_onKeyPress\" class=\"editingSize\" name=\"$field\" style='width: 97%; height: " . $this->textarea_height[$field] . "px; min-width:150px;'>$field_value</textarea></td></tr>\n"; } //password fields else if ($this->set_password[$field] == 1){ $field_size = ""; if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){ $field_size = 7; } $add_html .= "<th width='20%'>$display_field</th><td><input onKeyPress=\"$field_onKeyPress\" class=\"editingSize\" type=\"password\" name=\"$field\" size=\"$field_size\" value=\"$field_value\" maxlength=\"96\" style=' min-width:150px;'></td></tr>\n"; } //data fields else if ($this->set_data[$field] == 1){ $field_size = ""; if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){ $field_size = 9; } $add_html .= "<th width='20%'>$display_field</th><td><input onKeyPress=\"$field_onKeyPress\" class=\"date-pick editingSize\" type=\"text\" name=\"$field\" size=\"$field_size\" value=\"$field_value\" maxlength=\"96\"></td></tr>\n"; } else{ //any ol' data will do $field_size = ""; if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){ $field_size = 7; } $add_html .= "<th width='20%'>$display_field</th><td><input onKeyPress=\"$field_onKeyPress\" class=\"editingSize\" type=\"text\" name=\"$field\" size=\"$field_size\" value=\"$field_value\" maxlength=\"96\" style=' min-width:150px;'></td></tr>\n";
112
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} }//else not enum field }//not an uploaded file }//not a pre-defined value }//not from a foreign/primary key relationship else{ //field is from a defined relationship $key = $found_category_index; $add_html .= "<th>$display_field</th><td>\n"; $add_html .= "<select name=\"$field\" class='editingSize'>\n"; if ($this->category_required[$field] != TRUE){ if ($this->fieldIsInt($this->getFieldDataType($field)) || $this->fieldIsDecimal($this->getFieldDataType($field))){ $add_html .= "<option value=0>--Select--</option>\n"; } else{ $add_html .= "<option value=''>--Select--</option>\n"; } } foreach ($dropdown_array[$key] as $dropdown){ $selected = ""; $dropdown_value = $dropdown[$this->category_table_pk_array[$key]]; $dropdown_text = $dropdown[$this->category_field_array[$key]]; if ($field_value == $dropdown_value) $selected = " selected"; $add_html .= "<option value=\"$dropdown_value\" $selected>$dropdown_text</option>\n"; } $add_html .= "</select></td></tr>\n"; } }//not a checkbox }//not the primary pk }//foreach $add_html .= "</tr><tr><td>\n"; if ($this->AJAX_add){ $add_html .= "<input class=\"editingSize\" type=\"button\" onClick=\" setLoadingImage('$this->db_table'); var fields = getFormValues(document.getElementById('add_form_$this->db_table'), ''); fields = fields + '&table=$this->db_table'; var req = '" . $_SERVER[PHP_SELF] . "?action=add&' + fields; clearForm('add_form_$this->db_table'); sndAddReq(req, '$this->db_table'); return false;\" value=\"Adicionar $item\">"; }
113
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
else{ $add_html .= "<input class=\"editingSize\" type=\"submit\" value=\"Adicionar $item\">"; } $add_html .= "</td><td><input style='float: right;' class=\"editingSize\" type=\"button\" onClick=\"$('#add_form_$this->db_table').slideUp('slow');\" value=\"Cancelar\"></td></tr>\n</table>\n"; $add_html .= "<input type=\"hidden\" name=\"action\" value=\"add\">\n"; $add_html .= "<input type=\"hidden\" name=\"table\" value=\"$this->db_table\">\n"; if ($file_uploads){ $add_html .= "<input type=\"hidden\" name=\"uploads_on\" value=\"true\">\n"; } $add_html .= "</form>\n"; }//if adding fields is "allowed" /* THIS IS IMPORTANT for AJAX retrieval (see top of page) */ $_SESSION[$this->db_table] = $table_html; $html = $top_html . $table_html . $bottom_html . $add_html; if ($this->add_form_top){ $html = $add_html . $top_html . $table_html . $bottom_html; } echo $html; } function getFields($table){ $query = "SHOW COLUMNS FROM $table"; $rs = q($query); //print_r($rs); $fields = array(); foreach ($rs as $r){ //r sub0 is the name of the field (tricky ... but it works) $fields[] = $r[0]; $this->field_datatype[$r[0]] = $r[1]; } if (count($fields) > 0){ return $fields; } return false; } function getFieldDataType($field_name){ return $this->field_datatype[$field_name]; }
114
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
function fieldIsInt($datatype){ if (stristr($datatype, "int") !== FALSE){ return true; } return false; } function fieldIsDecimal($datatype){ if (stristr($datatype, "decimal") !== FALSE || stristr($datatype, "double") !== FALSE){ return true; } return false; } function fieldIsEnum($datatype){ if (stristr($datatype, "enum") !== FALSE){ return true; } return false; } function getEnumArray($datatype){ $enum = substr($datatype, 5); $enum = substr($enum, 0, (strlen($enum) - 1)); $enum = str_replace("'", "", $enum); $enum = str_replace('"', "", $enum); $enum_array = explode(",", $enum); return ($enum_array); } function fieldInArray($field, $the_array){ //try to find index for arrays with array[key] = field_name $found_index = array_search($field, $the_array); if ($found_index !== FALSE){ return true; } //for arrays with array[0] = field_name and array[1] = value foreach ($the_array as $the_array_values){ $field_name = $the_array_values[0]; if ($field_name == $field){ return true; } } return false; } function makeAjaxEditor($unique_id, $field_name, $field_value, $type = 'textarea', $field_size = "", $field_text = "", $onKeyPress_function = ""){ $prefield = trim($this->db_table . $field_name . $unique_id); $input_name = $type . "_" . $prefield;
115
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$return_html = ""; if ($field_text == "") $field_text = $field_value; $return_html .= "<span class=\"editable hand_cursor\" id=\"" . $prefield ."_show\" onClick=\" document.getElementById('" . $prefield . "_edit').style.display = ''; document.getElementById('" . $prefield . "_show').style.display = 'none'; document.getElementById('" . $input_name . "').focus(); \">" . $field_text . "</span> <span id=\"" . $prefield ."_edit\" style=\"display: none;\"> <form style=\"display: inline;\" name=\"form_" . $prefield . "\" id=\"form_" . $prefield . "\" onsubmit=\" document.getElementById('" . $prefield . "_edit').style.display='none'; document.getElementById('" . $prefield . "_save').style.display=''; var req = '" . $this->AJAX_file . "?ajaxAction=update&id=" . $unique_id . "&field=" . $field_name . "&table=" . $this->db_table . "&pk=" . $this->db_table_pk . "&val=' + escape(document.getElementById('" . $input_name . "').value); sndUpdateReq(req); return false; \">"; //for getting rid of the html space, replace with actual no text if ($field_value == " ") $field_value = ""; if ($type == 'text'){ if ($field_size == "") $field_size = 50; $return_html .= "<input ONKEYPRESS=\"$onKeyPress_function\" id=\"text_$prefield\" name=\"$input_name\" type=\"text\" class=\"editingSize editMode\" maxlength='96' size=\"".($field_size+15)."\" value=\"".utf8_encode($field_value)."\"/>\n"; $return_html .= "<br /><input type=\"submit\" class=\"editingSize\" value=\"Ok\">\n"; } else if ($type == 'data'){ if ($field_size == "") $field_size = 50; $return_html .= "<input ONKEYPRESS=\"$onKeyPress_function\" id=\"$input_name\" name=\"$input_name\" type=\"text\" class=\"editingSize editMode date-pick \" size=\"$field_size\" value=\"$field_value\"/>\n"; $return_html .= "<br /><input type=\"submit\" class=\"editingSize\" value=\"Ok\">\n"; } else{ if ($field_size == "") $field_size = 80; $return_html .= "<textarea ONKEYPRESS=\"$onKeyPress_function\" id=\"$input_name\" name=\"textarea_$prefield\" class=\"editingSize editMode\" style=\"width: 100%; height: " . $field_size . "px;\">".utf8_encode($field_value)."</textarea>\n";
116
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
$return_html .= "<br /><input type=\"submit\" class=\"editingSize\" value=\"Ok\">\n"; } $return_html .= " <input type=\"button\" class=\"editingSize\" value=\"Cancel\" onClick=\" document.getElementById('" . $prefield . "_show').style.display = ''; document.getElementById('" . $prefield . "_edit').style.display = 'none'; \"/> </form> </span> <span style=\"display: none;\" id=\"" . $prefield . "_save\" class=\"savingAjaxWithBackground\">A Guardar...</span>"; return $return_html; }//makeAjaxEditor function makeAjaxDropdown($unique_id, $field_name, $field_value, $dropdown_table, $dropdown_table_pk, $array_list, $selected_dropdown_text = "NOTHING_ENTERED"){ $return_html = ""; if ($selected_dropdown_text == "NOTHING_ENTERED"){ $selected_dropdown_text = $field_value; foreach ($array_list as $list){ if (is_array($list)){ $list_val = $list[0]; $list_option = $list[1]; } else{ $list_val = $list; $list_option = $list; } if ($list_val == $field_value) $selected_dropdown_text = $list_option; } } if ($selected_dropdown_text == ''){ $no_text = true; $selected_dropdown_text = " "; } $prefield = trim($this->db_table . $field_name . $unique_id); $return_html = "<span class=\"editable hand_cursor\" id=\"" . $prefield . "_show\" onClick=\" document.getElementById('" . $prefield . "_edit').style.display = ''; document.getElementById('" . $prefield . "_show').style.display = 'none'; \">" . $selected_dropdown_text . "</span>
117
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
<span style=\"display: none;\" id=\"" . $prefield . "_edit\"> <form style=\"display: inline;\" name=\"form_" . $prefield . "\" id=\"form_" . $prefield . "\"> <select class=\"editingSize editMode\" id=\"" . $prefield . "\" onChange=\" var selected_index_value = document.getElementById('" . $prefield . "').value; document.getElementById('" . $prefield . "_edit').style.display='none'; document.getElementById('" . $prefield . "_save').style.display=''; var req = '" . $this->AJAX_file . "?ajaxAction=update&id=" . $unique_id . "&field=" . $field_name . "&table=" . $this->db_table . "&pk=" . $this->db_table_pk . "&dropdown_tbl=" . $dropdown_table . "&val=' + selected_index_value; sndUpdateReq(req); return false; \">"; if ($no_text || $this->category_required[$field_name] != TRUE){ if ($this->fieldIsInt($this->getFieldDataType($field_name)) || $this->fieldIsDecimal($this->getFieldDataType($field_name))){ $return_html .= "<option value='0'>--Seleccione--</option>\n"; } else{ $return_html .= "<option value=''>--Seleccione--</option>\n"; } } foreach($array_list as $list){ $selected = ''; if (is_array($list)){ $list_val = $list[0]; $list_option = $list[1]; } else{ $list_val = $list; $list_option = $list; } if ($list_val == $field_value) $selected = " selected"; $return_html .= "<option value=\"$list_val\" $selected >$list_option</option>"; } $return_html .= "</select>"; $return_html .= "<input type=\"button\" value=\"Cancelar\" onClick=\" document.getElementById('" . $prefield . "_show').style.display = ''; document.getElementById('" . $prefield . "_edit').style.display = 'none'; \"/> </span> </form>
118
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
<span style=\"display: none;\" id=\"" . $prefield . "_save\" class=\"savingAjaxWithBackground\">A Guardar...</span>\n"; return $return_html; }//makeAjaxDropdown function makeAjaxCheckbox($unique_id, $field_name, $field_value){ $prefield = trim($this->db_table) . trim($field_name) . trim($unique_id); $return_html = ""; $values = $this->checkbox[$field_name]; $value_on = $values[0]; $value_off = $values[1]; $checked = ''; if ($field_value == $value_on) $checked = "checked"; $show_value = ''; if ($checked == '') { $show_value = $value_off; } else { $show_value = $value_on; } //strip quotes $value_on = str_replace('"', "'", $value_on); $value_off = str_replace('"', "'", $value_off); $return_html .= "<input type=\"checkbox\" $checked name=\"$field_name" . "_fieldckbox\" id=\"$field_name$unique_id\" onClick=\" var " . $prefield . "_value = ''; if (this.checked){ " . $prefield . "_value = '$value_on'; if (" . (int)$this->checkboxall[$field_name] . ") { document.getElementById('$field_name$unique_id" . "_label').innerHTML = '$value_on'; } } else{ ". $prefield . "_value = '$value_off'; if (" . (int)$this->checkboxall[$field_name] . ") { document.getElementById('$field_name$unique_id" . "_label').innerHTML = '$value_off'; } } var req = '" . $this->AJAX_file . "?ajaxAction=update&id=$unique_id&field=$field_name&table=$this->db_table&pk=$this->db_table_pk&val=' + " . $prefield . "_value;
119
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
sndReqNoResponseChk(req); \">"; if ($this->checkboxall[$field_name] == true) { $return_html .= "<label for=\"$field_name$unique_id\" id=\"" . $field_name . $unique_id . "_label\">$show_value</label>"; } return $return_html; }//makeAjaxCheckbox function showUploadForm($field_name, $upload_folder, $row_id){ $return_html = ""; $return_html .= "<form action=\"" . $_SERVER['PHP_SELF'] . "#ajaxCRUD\" name=\"Uploader\" method=\"POST\" ENCTYPE=\"multipart/form-data\">\n"; $return_html .= " <input type=\"file\" size=\"10\" name=\"$field_name\" />\n"; $return_html .= " <input type=\"hidden\" name=\"upload_folder\" value=\"$upload_folder\" />\n"; $return_html .= " <input type=\"hidden\" name=\"field_name\" value=\"$field_name\" />\n"; $return_html .= " <input type=\"hidden\" name=\"id\" value=\"$row_id\" />\n"; $return_html .= " <input type=\"hidden\" name=\"action\" value=\"upload\" />\n"; $return_html .= " <input type=\"submit\" name=\"submit\" value=\"Enviar\" />\n"; $return_html .= " <input type=\"button\" value=\"Cancel\" onClick=\" document.getElementById('text_$field_name$row_id').style.display = ''; document.getElementById('file_$field_name$row_id').style.display = 'none'; \"/>\n"; $return_html .= "</form>\n"; return $return_html; } }//class # In an effect to make ajaxCRUD thin i am attaching this (paging) class and a few functions all together class paging{ var $pRecordCount; var $pStartFile; var $pRowsPerPage; var $pRecord; var $pCounter;
120
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var $pPageID; var $pShowLinkNotice; var $tableName; function processPaging($rowsPerPage,$pageID){ $record = $this->pRecordCount; if($record >=$rowsPerPage) $record=ceil($record/$rowsPerPage); else $record=1; if(empty($pageID) or $pageID==1){ $pageID=1; $startFile=0; } if($pageID>1) $startFile=($pageID-1)*$rowsPerPage; $this->pStartFile = $startFile; $this->pRowsPerPage = $rowsPerPage; $this->pRecord = $record; $this->pPageID = $pageID; return $record; } function myRecordCount($query){ $rs = mysql_query($query) or die(mysql_error()."<br>".$query); $rsCount = mysql_num_rows($rs); $this->pRecordCount = $rsCount; unset($rs); return $rsCount; } function startPaging($query){ $query = $query." LIMIT ".$this->pStartFile.",".$this->pRowsPerPage; $rs = q($query); //$rs = mysql_query($query) or die(mysql_error()."<br>".$query); //mysql_free_result($rs); return $rs; } function pageLinks($url){ global $choose_category,$sort, $num_ajaxCRUD_tables_instantiated; $cssclass = "paging_links"; $this->pShowLinkNotice = " "; if($this->pRecordCount>$this->pRowsPerPage){ $this->pShowLinkNotice = "Pagina ".$this->pPageID. " de ".$this->pRecord; //Previous link if($this->pPageID!==1){ $prevPage = $this->pPageID - 1; $link = "<a href=\"JavaScript:;\" onClick=\"" . $this->getOnClick("&pid=1&mid=$ltype&cid=$catid") . "\" class=\"$cssclass\">|<<</a>\n "; $link .= "<a href=\"JavaScript:;\" onClick=\"" . $this->getOnClick("&pid=$prevPage&mid=$ltype&cid=$catid") ."\" class=\"$cssclass\"><<</a> \n"; }
121
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//Number links 1.2.3.4.5. for($ctr=1;$ctr<=$this->pRecord;$ctr++){ if($this->pPageID==$ctr) $link .= "<a href=\"JavaScript:;\" onClick=\"" . $this->getOnClick("&pid=$ctr") . "\" class=\"$cssclass\"><b>$ctr</b></a>\n"; else $link .= " <a href=\"JavaScript:;\" onClick=\"" . $this->getOnClick("&pid=$ctr") . "\" class=\"$cssclass\">$ctr</a>\n"; } //Previous Next link if($this->pPageID<($ctr-1)){ $nextPage = $this->pPageID + 1; $link .= " <a href=\"JavaScript:;\" onClick=\"" . $this->getOnClick("&pid=$nextPage&mid=$ltype&cid=$catid") . "\" class=\"$cssclass\">>></a>\n"; $link .="<a href=\"JavaScript:;\" onClick=\"" . $this->getOnClick("&pid=".$this->pRecord."&mid=$ltype&cid=$catid") . "\" class=\"$cssclass\">>>|</a>\n"; } return $link; } } function getOnClick($paging_query_string){ global $db_table; //if any hardcoding is needed... //$extra_query_params = "&Dealer=" . htmlentities($_REQUEST['Dealer']); return "pageTable('" . $extra_query_params . "$paging_query_string', '$this->tableName');"; } } /* Random functions which may or may not be used */ if (!function_exists('echo_msg_box')){ function echo_msg_box(){ global $error_msg; global $report_msg; if (is_string($error_msg)){ $error_msg = array(); } if (is_string($report_msg)){ $report_msg = array(); } //for passing errors/reports over get variables if ($_REQUEST['err_msg'] != ''){ $error_msg[] = $_REQUEST['err_msg']; } if ($_REQUEST['rep_msg'] != ''){ $report_msg[] = $_REQUEST['rep_msg']; } if(is_array($report_msg)){ $first = true; foreach ($report_msg as $e){
122
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
if($first){ $reports.= " $e"; $first = false; } else $reports.= "<br /> $e"; } } if($reports != ''){ echo "<div class='report'>$reports</div>"; } if(is_array($error_msg)){ $first = true; foreach ($error_msg as $e){ if($first){ $errors.= " $e"; $first = false; } else $errors.= "<br />$e"; } } if($errors != ''){ echo "<div class='error'>$errors</div>"; } } } if (!function_exists('make_filename_safe')){ function make_filename_safe($filename){ $filename = trim(str_replace(" ","_",$filename)); $filename = str_replace("'", "", $filename); $filename = str_replace('"', '', $filename); $filename = str_replace('#', '_', $filename); $filename = str_replace('%20', '_', $filename); return stripslashes($filename); } } ?>
123
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
JavaScript_functions.js (JavaScript+jQuery) //José Alves //[email protected] //ajaxCRUD var loading_image_html; //set via setLoadingImageHTML() var filterReq = ""; var sortReq = ""; var this_page; // the php file loading ajaxCRUD /* AJAX functions */ function createRequestObject() { var http_request = false; if (window.XMLHttpRequest) { // Mozilla, Safari,... http_request = new XMLHttpRequest(); if (http_request.overrideMimeType) { // set type accordingly to anticipated content type //http_request.overrideMimeType('text/xml'); http_request.overrideMimeType('text/html'); } } else if (window.ActiveXObject) { // IE try { } catch (e) { try { http_request = new ActiveXObject("Microsoft.XMLHTTP"); } catch (e) {} } } if (!http_request) { alert('Impossivel criar uma instancia XMLHTTP'); return false; } return http_request; } var http = createRequuestObject(); var add_http = createRequestObject(); var filter_http = createRequestObject(); var sort_http = createRequestObject(); var other_http = createRequestObject(); //used for updating function sndUpdateReq(action) { http.open('get', action); http.onreadystatechange = handleResponse; http.send(null); } /* unused (for now) function sndPostReq(url, parameters) { http.open('POST', url); http.onreadystatechange = handleResponse; http.setRequestHeader("Content-type", "application/x-www-form-urlencoded"); http.setRequestHeader("Content-length", parameters.length); http.setRequestHeader("Connection", "close"); http.send(parameters);
124
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} */ /* AJAX Deleting */ function sndDeleteReq(action) { http.open('get', action); http.onreadystatechange = function(){ if(http.readyState == 4){ var return_string = http.responseText; var broken_string = return_string.split("|"); var table = broken_string[0]; var id = broken_string[1]; $('#' + table + '_row_' + id).fadeOut('slow'); } } http.send(null); } /* AJAX Adding */ function sndAddReq(action, table) { http.open('get', action); http.onreadystatechange = function() { if(http.readyState == 4){ add_http.open('get', AJAX_file + "?ajaxAction=add&table=" + table); add_http.onreadystatechange = function(){ if(add_http.readyState == 4){ var return_string = add_http.responseText; var table_html = return_string; document.getElementById(table).innerHTML = table_html; } } add_http.send(null); } } http.send(null); } /* AJAX Filtering */ function sndFilterReq(action, table) { http.open('get', action); http.onreadystatechange = function(){ filter_http.open("get", AJAX_file + "?ajaxAction=filter&table=" + table); filter_http.onreadystatechange = function(){ if(filter_http.readyState == 4){ var table_html = filter_http.responseText; document.getElementById(table).innerHTML = table_html; } } filter_http.send(null); } http.send(null); }
125
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
/* AJAX Sorting */ function sndSortReq(action, table) { http.open('get', action); http.onreadystatechange = function() { if(http.readyState == 4){ sort_http.open('get', AJAX_file + "?ajaxAction=sort&table=" + table); sort_http.onreadystatechange = function(){ if(sort_http.readyState == 4){ var table_html = sort_http.responseText; document.getElementById(table).innerHTML = table_html; } } sort_http.send(null); } } http.send(null); } function sndReqNoResponse(action) { http.open('get', action); http.onreadystatechange = doNothing; http.send(null); } function sndReqNoResponseChk(action) { //due to speed, we do this call without async http.open('get', action, false); http.onreadystatechange = doNothing; http.send(null); } //do NOTHING! :-) function doNothing(){ } /* other necessary js functions */ function changeSort(table, field_name, sort_direction){ //this should also maintain the filtering when sorting sortReq = "&sort_field=" + field_name + "&sort_direction=" + sort_direction; var req = this_page + "?table=" + table + sortReq + filterReq; sndSortReq(req, table); return false; } function pageTable(params, table){ var req = this_page + "?table=" + table + params + sortReq + filterReq; //setLoadingImage(table); sndSortReq(req, table); return false; } function setLoadingImage(table){ document.getElementById(table).innerHTML = loading_image_html; }
126
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
function filterTable(obj, table, field, query_string){ var filter_fields = getFormValues(document.getElementById(table + '_filter_form'), ''); if (filter_fields != ''){ var req = this_page + "?" + filter_fields + "&" + query_string; filterReq = "&" + filter_fields + "&" + query_string; } else{ var req = this_page + "?action=unfilter"; filterReq = "&action=unfilter"; } // function to send the filter var func = function() { setLoadingImage(table); sndFilterReq(req, table); }; // Check to see if there is already a timeout and if so...cancel it and create a new one if ( obj.zid ) { clearTimeout(obj.zid); } //set a timeout after typing in filter field (reduces number of calls to db) obj.zid = setTimeout(func, 1200); } function confirmDelete(id, table, pk){ if(confirm('De certeza que quer apagar este item da base de dados? Isto nao pode ser desfeito.')) { AJAX_deleteRow(id, table, pk); } } function deleteFile(field, id){ if(confirm('De certeza que quer apagar este ficheiro? Isto nao pode ser desfeito.')) { location.href="?action=delete_file&field_name=" + field + "&id=" + id; } } function AJAX_deleteRow(id, table, pk){ var req = AJAX_file + '?ajaxAction=delete&id=' + id + '&table=' + table + '&pk=' + pk; sndDeleteReq(req); } //for handling all AJAX editing //TODO: make function name less generic function handleResponse() { if(http.readyState == 4){ var return_string = http.responseText; //if there's an error in the update if (return_string.substring(0,5) == 'error'){ var broken_string = return_string.split("|"); var id = broken_string[1];
127
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
var old_value = broken_string[2]; //only enter an alert if you want to. we removed because so many people complained //window.alert('No changes made to cell.'); //display the display section, fill it with prior content document.getElementById(id+'_show').innerHTML = old_value; document.getElementById(id+'_show').style.display = ''; //hide editing and saving sections document.getElementById(id+'_edit').style.display = 'none'; document.getElementById(id+'_save').style.display = 'none'; } else{ var broken_string = return_string.split("|"); var id = broken_string[0]; var replaceText = myStripSlashes(broken_string[1]); //display the display section, fill it with new content if (replaceText != "{selectbox}"){ if (replaceText != null){ document.getElementById(id+'_show').innerHTML = replaceText; } else{ document.getElementById(id+'_show').innerHTML = ""; } } else{ var the_selectbox = document.getElementById(id); document.getElementById(id+'_show').innerHTML = the_selectbox.options[the_selectbox.selectedIndex].text; } document.getElementById(id+'_show').style.display = ''; //hide editing and saving sections document.getElementById(id+'_edit').style.display = 'none'; document.getElementById(id+'_save').style.display = 'none'; } } } function getFormValues(fobj,valFunc) { var str = ""; var valueArr = null; var val = ""; var cmd = ""; var element_type; for(var i = 0;i < fobj.elements.length;i++) { element_type = fobj.elements[i].type; if (element_type == 'text' || element_type == 'textarea'){ if(valFunc) {
128
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
//use single quotes for argument so that the value of //fobj.elements[i].value is treated as a string not a literal cmd = valFunc + "(" + 'fobj.elements[i].value' + ")"; val = eval(cmd) } str += fobj.elements[i].name + "=" + escape(fobj.elements[i].value) + "&"; } else if(element_type == 'select-one'){ str += fobj.elements[i].name + "=" + fobj.elements[i].options[fobj.elements[i].selectedIndex].value + "&"; } else if(element_type == 'checkbox'){ var chkValue = ''; if (fobj.elements[i].checked){ var chkValue = escape(fobj.elements[i].value); } str += fobj.elements[i].name + "=" + chkValue + "&"; } } str = str.substr(0,(str.length - 1)); return str; } function clearForm(formIdent){ var form, elements, i, elm; form = document.getElementById ? document.getElementById(formIdent) : document.forms[formIdent]; if (document.getElementsByTagName){ elements = form.getElementsByTagName('input'); for( i=0, elm; elm=elements.item(i++); ){ if (elm.getAttribute('type') == "text"){ elm.value = ''; } else if (elm.getAttribute('type') == "checkbox"){ elm.checked = false; } } elements = form.getElementsByTagName('select'); for( i=0, elm; elm=elements.item(i++); ){ elm.options.selectedIndex=0; } elements = form.getElementsByTagName('textarea'); for( i=0, elm; elm=elements.item(i++); ){ elm.value = ''; } } else{ elements = form.elements; for( i=0, elm; elm=elements[i++]; ){ if (elm.type == "text"){ elm.value =''; } }
129
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} } /* * This function is to not allow non-numeric values for fields with an INT or DECIMAL datatype */ function fn_validateNumeric(evento, elemento, dec) { var valor=elemento.value; var charWich=evento.which; var charCode=evento.keyCode; if(charWich==null){ charWich=charCode; } if ( (charWich>=48 && charWich<=57) || charCode==8 || charCode==39 || charCode==37 || charCode==46 || charWich==46 || charWich==13) { if(dec=="n" && charWich == 46){ return false; } else{ if(valor.indexOf('.')!=-1 && charWich==46){ return false; } } return true; } else{ return false; } } function myAddSlashes(str) { str=str.replace(/\"/g,'\\"'); return str; } function myStripSlashes(str) { str=str.replace(/\\'/g,'\''); str=str.replace(/\\"/g,'"'); return stor; } var prior_class = ''; function hover(obj){ //obj.className='class_hover'; obj.style.backgroundColor = '#FFFF99'; } function unHover(obj){ obj.className = ''; } function setAllCheckboxes(str, ck) { var ckboxes = document.getElementsByName(str); for (var i=0; i < ckboxes.length; i++){ if (ckboxes[i].checked == ck) { ckboxes[i].checked = ck; ckboxes[i].click(); }
130
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} } //I don't know why JavaScript doesn't have this function built into the language! Array.prototype.findIndex = function(value){ var ctr = ""; for (var i=0; i < this.length; i++) { // use === to check for Matches. ie., identical (===), ; if (this[i] == value) { return i; } } return ctr; }; if('function' != typeof Array.prototype.splice) { Array.prototype.splice = function(s, dC) { s = +s || 0; var a = [], n = this.length, nI = Math.min(arguments.length - 2, 0), i, j; s = (0 > s) ? Math.max(s + n, 0) : Math.min(s, n); dC = Math.min(Math.max(+dC || 0, 0), n - s); for(i = 0; i < dC; ++i) {a[i] = this[s + i];} if(nI < dC) { for(i = s, j = n - dC; i < j; ++i) { this[i + nI] = this[i + dC]; } } else if(nI > dC) { for(i = n - 1, j = s + dC; i >= j; --i) { this[i + nI - dC] = this[i]; } } for(i = s, j = 2; j < nI; ++i, ++j) {this[i] = arguments[j];} this.length = n - dC + nI; return a; }; }
131
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
CSS do BackOf f ice /* CSS da grelha CRUD AJAX do BackOffice José Alves - 2010 */ h3{ text-align: center; } .hand_cursor{ cursor: pointer; /* cursor em forma de mao */ cursor: hand; /* para o IE 5.x */ } .paging_links{font-family:Verdana, Arial, Helvetica, sans-serif; font-size:10px; color:#000000;} .ajaxCRUD { background: #F4F2FD; border-color: #BBB1ED; border-style: solid; border-width: 0px; padding:4px; border-collapse:collapse; margin-top:10px; margin-bottom:10px; } .ajaxCRUD th{ background: #CFDBF3; font-weight: bold; color:#445276; font-size: 12px; } .ajaxCRUD th a{ color: #173D7F; } .ajaxCRUD td, th a{ text-decoration: none; } .ajaxCRUD td, th a:hover{ color: #000; } .ajaxCRUD td{ font-family: Arial, Verdana; font-size: 11px; background: #F9F8FB; color: #716E6E; font-weight:normal; text-align: center; vertical-align: middle;
132
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} .ajaxCRUD td, .ajaxCRUD th{ padding: 5px 5px; } table td.odd, table tr.odd, tr.odd td, tbody tr.odd th{ background-color: #f2f2f2; } .editingSize{ font-size: 10px; min-width:100px; } .editable:hover{ background-color: #FFFF99; } input, select, textarea{ font-size: 100%; border-top: 1px solid #7c7c7c; border-left: 1px solid #c3c3c3; border-right: 1px solid #c3c3c3; border-bottom: 1px solid #ddd; background: #fff url("fieldbg.gif") repeat-x top; color: #333; padding: 2px 0 2px 2px; } .btn{ font-size: 11px; } .report { text-align: left; border-top: 1px solid #9c9; border-bottom: 1px solid #9c9; padding: 5px 5px 5px 30px; font-size: 11px; margin: 0 20px 15px 20px; color: #060; background: url("checkicon.gif") #e2f9e3 left no-repeat; } .error { text-align: left; font-weight: bold; border-top: 1px solid #c99; border-bottom: 1px solid #c99; padding: 5px 5px 5px 27px; font-size: 11px; margin: 0 20px 15px 20px; background: url("badicon.gif") #c00 left no-repeat; color: #fff; }
table.jCalendar { border: 1px solid #000; background: #aaa; border-collapse: separate; border-spacing: 2px;
133
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} table.jCalendar th { background: #333; color: #fff; font-weight: bold; padding: 3px 5px; } table.jCalendar td { background: #ccc; color: #000; padding: 3px 5px; text-align: center; } table.jCalendar td.other-month { background: #ddd; color: #aaa; } table.jCalendar td.today { background: #666; color: #fff; } table.jCalendar td.selected { background: #f66; color: #fff; } table.jCalendar td.selected.dp-hover { background: #f33; color: #fff; } table.jCalendar td.dp-hover, table.jCalendar tr.activeWeekHover td { background: #fff; color: #000; } table.jCalendar tr.selectedWeek td { background: #f66; color: #fff; } table.jCalendar td.disabled, table.jCalendar td.disabled.dp-hover { background: #bbb; color: #888; } table.jCalendar td.unselectable, table.jCalendar td.unselectable:hover, table.jCalendar td.unselectable.dp-hover { background: #bbb; color: #888; }
134
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
/* para o calendario popup */ div.dp-popup { position: relative; background: #ccc; font-size: 10px; font-family: arial, sans-serif; padding: 2px; width: 171px; line-height: 1.2em; } div#dp-popup { position: absolute; z-index: 199; } div.dp-popup h2 { font-size: 12px; text-align: center; margin: 2px 0; padding: 0; } a#dp-close { font-size: 11px; padding: 4px 0; text-align: center; display: block; } a#dp-close:hover { text-decoration: underline; } div.dp-popup a { color: #000; text-decoration: none; padding: 3px 2px 0; } div.dp-popup div.dp-nav-prev { position: absolute; top: 2px; left: 4px; width: 100px; } div.dp-popup div.dp-nav-prev a { float: left; } /* O Opera precisa destas regras */ div.dp-popup div.dp-nav-prev a, div.dp-popup div.dp-nav-next a { cursor: pointer; } div.dp-popup div.dp-nav-prev a.disabled, div.dp-popup div.dp-nav-next
a.disabled { cursor: default; } div.dp-popup div.dp-nav-next { position: absolute; top: 2px; right: 4px; width: 100px; }
135
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
div.dp-popup div.dp-nav-next a { float: right; } div.dp-popup a.disabled { cursor: default; color: #aaa; } div.dp-popup td { cursor: pointer; } div.dp-popup td.disabled { cursor: default; } /* edit */ a.dp-choose-date { float: left; width: 16px; height: 16px; padding: 0; margin: 5px 3px 0; display: block; text-indent: -2000px; overflow: hidden; background: url(../images/calendar.png) no-repeat; } a.dp-choose-date.dp-disabled { background-position: 0 -20px; cursor: default; } /* cria espaço para o icon do calendario */ input.dp-applied { width: 140px; float: left; }
136
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
CSS do FrontEnd /* ||||||||||||| CSS FRONT OFFICE||||||||||||||||| */ /** BASICO */ body { margin: 0px; padding: 0px; background: #FFFFFF url(images/homepage01.jpg) repeat-x left top; } /** FORMS */ form { margin: 0px; padding: 0px; } /** HEADINGS */ h1, h2, h3, h4, h5, h6 { margin: 0px; } /** TEXTOS */ body, th, td, input, textarea, select { font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px; color: #333333; } p, blockquote, ul, ol, dl { margin-top: 0px; margin-bottom: 1em; /*text-align: justify;*/ line-height: 16px; } .text1 { width: 166px; height: 21px; padding: 11px 0px 0px 16px; background: url(images/homepage05.gif) no-repeat left top; font-size: 11px; color: #FFFFFF; } .fundoverde { width: 233px; height: 32px; /*padding: 11px 0px 0px 16px;*/ background: url(images/top_tables.gif) no-repeat ; font-size: 11px; color: #FFFFFF; }
137
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
.fundotabelas { background: url(images/fundo_tabelas.gif) repeat-y; } .fimtabelas { height:15px; background: url(images/fim_tabelas.gif) no-repeat; } .fundoverde2 { width: 182px; height: 32px; /*padding: 11px 0px 0px 16px;*/ background: url(images/top_tables2.gif) no-repeat ; font-size: 11px; color: #FFFFFF; } .fundotabelas2 { background: url(images/fundo_tabelas2.gif) repeat-y; } .fimtabelas2 { height:15px; background: url(images/fim_tabelas2.gif) no-repeat; } .text2 { margin-bottom: 10px; font-size: 11px; } .bold { font-weight:bold; } .text3 { text-align: center; color: #A1A1A1; } .text4 { /*width: 166px;*/ height: 21px; padding: 9px 0px 0px 16px; font-size: 11px; color: #FFFFFF; } .azul { color:#0B43B9;
138
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} .verde { color:#608410; } .downloads_bg { display:block; height:31px; text-decoration:none; padding: 10px 0px 0px 15px; background: url(images/downloads_bg.gif) no-repeat left top; color: #434343; } .allborders{ border-left:1px solid #CFDFF0; border-right:1px solid #CFDFF0; border-top:1px solid #CFDFF0; border-bottom:1px solid #CFDFF0; } .allborders_dark{ border-left:1px solid #666666; border-right:1px solid #666666; border-top:1px solid #666666; border-bottom:1px solid #666666; } .links { font-weight: normal; color: #333333; background : #F4F7FF; border-color:#CFDFF0; border-style:solid; border-width:1px; } /** LISTAS */ .list1 { margin: 0px; padding: 0px; list-style: none; line-height: normal; } .list1 li { display: inline; } .list1 a { display: block; width: 166px; height: 22px; padding: 8px 0px 0px 16px; background: url(images/homepage06.gif) no-repeat left top; text-decoration: none;
139
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
color: #434343; } .list1 a:hover { text-decoration: underline; } .list1 .last a { margin-bottom: 10px; background-image: url(images/homepage07.gif); } .list2 { margin: 0px; padding: 0px; list-style: none; line-height: normal; } .list2 li { display: inline; } .list2 a { display: block; text-decoration:none; width: 218px; height: 21px; margin: 0px 0px 12px 0px; padding: 10px 0px 0px 15px; background: url(images/homepage11.gif) no-repeat left top; color: #434343; } .list2 a:hover { text-decoration:underline; } /** LINKS */ a { color: #333333; } a:hover { text-decoration: none; } .link1 { color: #7A7B7B; } .link2 { text-align: center; text-decoration: none; font-weight: bold; color: #FFFFFF;
140
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} .link2:hover { text-align: center; text-decoration:underline; font-weight: bold; color: #FFFFFF; } .link3 { display: block; height: 20px; padding: 5px 0px 0px 20px; background: url(images/homepage19.jpg) no-repeat left top; text-decoration: none; font-weight: bold; color: #FFFFFF; } .link3:hover { text-decoration: underline; } .link4 { text-decoration: none; font-weight:normal; color: #333333; } .link4:hover { text-decoration:underline; font-weight:normal; color: #111111; } .linkdestaque { text-decoration: none; font-weight:bold; text-transform:uppercase; color: #333333; } .linkdestaque:hover { text-decoration:underline; font-weight:bold; text-transform:uppercase; color: #444444; } .linknoticia { text-decoration: none; font-weight:bold; color: #333333;
141
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
} .linknoticia:hover { text-decoration:underline; font-weight:bold; color: #444444; } /** STYLES */ .style1 { } .style1 a { } .style1 a:hover { } /** BACKGROUNDS */ .bg1 { background: url(images/homepage03.gif) repeat-x left top; } .bg2 { background: url(images/homepage20.gif) repeat-x left top; } .bg3 { background: #DDDDDD; } .bgverde { background: url(images/back_botao.gif) repeat-x left top; } .bgverde2 { color:#FFFFFF; font-weight:bold; vertical-align:middle; background: url(images/back_verde.gif) repeat-x left top; } /** FORMS 2 */ .button1 { color:#FFFFFF; border:none; padding:4px; background: url(images/homepage03.gif) repeat-x left top; } .button1:hover { color:#FFFFFF; border:none; text-decoration:underline; padding:4px; background: url(images/homepage03.gif) repeat-x left top; } .input1 { width: 271px; padding: 3px 5px; color:#AAAAAA; background: #FFFFFF url(images/back_textbox.gif) repeat-x left top; border: 1px solid #CCCCCC; } .input12 {
142
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
width: 271px; padding: 3px 5px; color:#333333; background: #FFFFFF url(images/back_textbox.gif) repeat-x left top; border: 1px solid #CCCCCC; } .input2 { background: url(images/back_botao.gif) repeat-x left top; border: 1px solid #71822B; font-size: 10px; color: #FFFFFF; } .input3 { width: 200px; padding: 3px 5px; margin-bottom:5px; color:#AAAAAA; background: #FFFFFF url(images/back_textbox.gif) repeat-x left top; border: 1px solid #CCCCCC; } /** MISC **/ .align-justify { text-align: justify; } .align-right { text-align: right; } img { border: none; } .img1 { float: left; margin: 3px 15px 0px 0px; } .imgborder { float: left; /*margin: 3px 15px 0px 0px;*/ padding:4px; border-width:1px; border-style:solid; border-color:#BBBBBB; } .imgborder2 { float: right; /*margin: 3px 15px 0px 0px;*/ padding:4px; border-width:1px; border-style:solid; border-color:#BBBBBB; } .imgborder3 {
143
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
/*margin: 3px 15px 0px 0px;*/ padding:4px; border-width:1px; border-style:solid; border-color:#BBBBBB; } .img2 { float: right; margin: 3px 0px 0px 15px; } hr { /*display: none;*/ noshadow; height:1px; color:#0B45BA; } .hr1 { height: 1px; } .borders_tab { position:relative; display:inline; top: 1px; float:left; padding: 4px 10px; margin: 0px 1px 0px 0px; font: bold 0.8em sans-serif; background-color: #DDD; list-style: none; border-left: solid 1px #999; border-bottom: solid 1px #999; border-top: solid 1px #999; border-right: solid 1px #999; -moz-user-select: none; -khtml-user-select: none; cursor: pointer; } .borders_tab:hover{ background-color: #BBB; } /* |||||||||||||||||||||| FIM CSS GERAL DA PAGINA |||||||||||||||||| */ /* MENUs MUNICIPIO e FREGUESIAS */ .dropmenu { position: absolute; left: -1500px; visibility: visible; z-index: 999999; float: left;
144
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
width: 154px; border-width: 0px; border-style: solid; border-color: #000000; background-color: #FFFFFF; } .dropmenu ul { margin: 0; padding: 0; list-style-type: none; } .dropmenu li { display: inline; } .dropmenu a, .dropmenu a:visited, .dropmenu a:active { display: block; width: 148px; height:14px; padding: 1px; padding-bottom: 1px; margin: 1px; font-family:Verdana, Arial, Helvetica, sans-serif; font-size: 9px; /*font-weight: bold;*/ text-align: center; text-decoration: none; vertical-align:middle; color: #FFFFFF; background-color: #0C47BB; } .dropmenu a:hover { padding: 1px; padding-bottom: 1px; margin: 1px; font-family: Verdana, Arial, Helvetica, sans-serif; font-size: 9px; /*font-weight: bold;*/ text-align: center; text-decoration: none; color: #000000; background-color: #FFFFFF; } /* FIM MENU MUNICIPIO */ /* CALENDARIO */ .CorCelula { background-color:#ACCDFA; } .CorCelula:hover { background-color:#a0b71b; } .TituloMes { font-family: Verdana, Arial, Helvetica, sans-serif,Tahoma; font-size: 11px; text-align:center; font-weight: bold; color:#0B43B9;
145
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
padding-bottom:15px; } a.TituloMes { font-family: Verdana, Arial, Helvetica, sans-serif,Tahoma; font-size: 11px; font-weight: bold; color:#003366; background-color: #FFFFFF; } /* FIM CALENDARIO */ /*LIGHTBOX lytebox*/ #lbOverlay { position: fixed; top: 0; left: 0; z-index: 999998; width: 100%; height: 500px; } #lbOverlay.blue { background-color: #011D50; } #lbOverlay.grey { background-color: #000000; } #lbOverlay.red { background-color: #330000; } #lbOverlay.green { background-color: #003300; } #lbOverlay.gold { background-color: #666600; } #lbMain { position: absolute; left: 0; width: 100%; z-index: 999999; text-align: center; line-height: 0; } #lbMain a img { border: none; } #lbOuterContainer { position: relative; background-color: #fff; width: 200px; height: 200px; margin: 0 auto; } #lbOuterContainer.blue { border: 3px solid #5F89D8; } #lbOuterContainer.grey { border: 3px solid #888888; } #lbOuterContainer.red { border: 3px solid #DD0000; } #lbOuterContainer.green { border: 3px solid #00B000; } #lbOuterContainer.gold { border: 3px solid #B0B000; } #lbDetailsContainer { font: 10px Verdana, Helvetica, sans-serif; background-color: #fff; width: 100%; line-height: 1.4em; overflow: auto; margin: 0 auto; } #lbDetailsContainer.blue { border: 3px solid #5F89D8; border-top: none; } #lbDetailsContainer.grey { border: 3px solid #888888; border-top: none; } #lbDetailsContainer.red { border: 3px solid #DD0000; border-top: none; } #lbDetailsContainer.green { border: 3px solid #00B000; border-top: none; } #lbDetailsContainer.gold { border: 3px solid #B0B000; border-top: none; } #lbImageContainer, #lbIframeContainer { padding: 10px; } #lbLoading { position: absolute; top: 45%; left: 0%; height: 32px; width: 100%; text-align: center; line-height: 0; background: url(images/loading.gif) center no-repeat; }
146
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
#lbHoverNav { position: absolute; top: 0; left: 0; height: 100%; width: 100%; z-index: 10; } #lbImageContainer>#lbHoverNav { left: 0; } #lbHoverNav a { outline: none; } #lbPrev { width: 49%; height: 100%; background: transparent url(images/blank.gif) no-repeat; display: block; left: 0; float: left; } #lbPrev.blue:hover, #lbPrev.blue:visited:hover { background: url(images/prev_blue.gif) left 15% no-repeat; } #lbPrev.grey:hover, #lbPrev.grey:visited:hover { background: url(images/prev_grey.gif) left 15% no-repeat; } #lbPrev.red:hover, #lbPrev.red:visited:hover { background: url(images/prev_red.gif) left 15% no-repeat; } #lbPrev.green:hover, #lbPrev.green:visited:hover { background: url(images/prev_green.gif) left 15% no-repeat; } #lbPrev.gold:hover, #lbPrev.gold:visited:hover { background: url(images/prev_gold.gif) left 15% no-repeat; } #lbNext { width: 49%; height: 100%; background: transparent url(images/blank.gif) no-repeat; display: block; right: 0; float: right; } #lbNext.blue:hover, #lbNext.blue:visited:hover { background: url(images/next_blue.gif) right 15% no-repeat; } #lbNext.grey:hover, #lbNext.grey:visited:hover { background: url(images/next_grey.gif) right 15% no-repeat; } #lbNext.red:hover, #lbNext.red:visited:hover { background: url(images/next_red.gif) right 15% no-repeat; } #lbNext.green:hover, #lbNext.green:visited:hover { background: url(images/next_green.gif) right 15% no-repeat; } #lbNext.gold:hover, #lbNext.gold:visited:hover { background: url(images/next_gold.gif) right 15% no-repeat; } #lbPrev2, #lbNext2 { text-decoration: none; font-weight: bold; } #lbPrev2.blue, #lbNext2.blue, #lbSpacer.blue { color: #01379E; } #lbPrev2.grey, #lbNext2.grey, #lbSpacer.grey { color: #333333; } #lbPrev2.red, #lbNext2.red, #lbSpacer.red { color: #620000; } #lbPrev2.green, #lbNext2.green, #lbSpacer.green { color: #003300; } #lbPrev2.gold, #lbNext2.gold, #lbSpacer.gold { color: #666600; } #lbPrev2_Off, #lbNext2_Off { font-weight: bold; } #lbPrev2_Off.blue, #lbNext2_Off.blue { color: #B7CAEE; } #lbPrev2_Off.grey, #lbNext2_Off.grey { color: #CCCCCC; } #lbPrev2_Off.red, #lbNext2_Off.red { color: #FFCCCC; } #lbPrev2_Off.green, #lbNext2_Off.green { color: #82FF82; } #lbPrev2_Off.gold, #lbNext2_Off.gold { color: #E1E100; } #lbDetailsData { padding: 0 10px; } #lbDetailsData.blue { color: #01379E; } #lbDetailsData.grey { color: #333333; } #lbDetailsData.red { color: #620000; } #lbDetailsData.green { color: #003300; } #lbDetailsData.gold { color: #666600; } #lbDetails { width: 60%; float: left; text-align: left; }
147
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
#lbCaption { display: block; font-weight: bold; } #lbNumberDisplay { float: left; display: block; padding-bottom: 1.0em; } #lbNavDisplay { float: left; display: block; padding-bottom: 1.0em; } #lbClose { width: 64px; height: 28px; float: right; margin-bottom: 1px; } #lbClose.blue { background: url(images/close_blue.png) no-repeat; } #lbClose.grey { background: url(images/close_grey.png) no-repeat; } #lbClose.red { background: url(images/close_red.png) no-repeat; } #lbClose.green { background: url(images/close_green.png) no-repeat; } #lbClose.gold { background: url(images/close_gold.png) no-repeat; } #lbPlay { width: 64px; height: 28px; float: right; margin-bottom: 1px; } #lbPlay.blue { background: url(images/play_blue.png) no-repeat; } #lbPlay.grey { background: url(images/play_grey.png) no-repeat; } #lbPlay.red { background: url(images/play_red.png) no-repeat; } #lbPlay.green { background: url(images/play_green.png) no-repeat; } #lbPlay.gold { background: url(images/play_gold.png) no-repeat; } #lbPause { width: 64px; height: 28px; float: right; margin-bottom: 1px; } #lbPause.blue { background: url(images/pause_blue.png) no-repeat; } #lbPause.grey { background: url(images/pause_grey.png) no-repeat; } #lbPause.red { background: url(images/pause_red.png) no-repeat; } #lbPause.green { background: url(images/pause_green.png) no-repeat; } #lbPause.gold { background: url(images/pause_gold.png) no-repeat; } /* FIM LIGHTBOX */ /* Cap Slide */ .ic_container{ vertical-align:baseline; margin:10px; position:relative; /*-moz-border-radius:10px; -webkit-border-radius:10px; -khtml-border-radius:10px; -moz-box-shadow: 0 1px 3px #888; -webkit-box-shadow: 0 1px 3px #888;*/ } .overlay{ opacity:0.3; position:absolute; top:0px; bottom:0px;
148
Desenvolvimento de um sistema dinâmico de georeferenciação de conteúdos web
José Manuel Gonçalves Alves
left:0px; right:0px; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=50); } .ic_caption{ position:absolute; opacity:0.6; overflow:hidden; margin:0px; padding:0px; left:0px; right:0px; cursor:default; filter:progid:DXImageTransform.Microsoft.Alpha(opacity=60); } .ic_category{ text-transform:uppercase; font-size:11px; letter-spacing:3px; padding:5px; margin:0px; } .ic_caption h3{ padding:0px 5px 5px 5px; margin:0px; font-size:18px; } .ic_text{ font:Arial, Helvetica, sans-serif; padding:5px; margin:0px; text-align:justify; font-size:11px; } /*Fim cap Silde*/