//TODO documentar //XODO permitir edicao de temas editaveis, nao apenas de regioes cadastradas //XODO permitir a edicao de dados fora do esquema metaestat /* Editor vetorial de limites para a interface google maps Utilizado em i3geo/metaestat/editorlimites.php Utiliza a API do Google Maps e pacotes/wicket/wicket.js (que cria o objeto Wkt com funcoes para processamento de Wkt) Licenca: GPL2 i3Geo Interface Integrada de Ferramentas de Geoprocessamento para Internet Direitos Autorais Reservados (c) 2006 Ministério do Meio Ambiente Brasil Desenvolvedor: Edmar Moretti edmar.moretti@gmail.com Esse programa utiliza parcialmente os codigos da aplicacao calculadora de carbono desenvolvido pelo IPAM - Instituto de Pesquisa Ambiental da Amazonia Este programa e software livre; voce pode redistribui-lo e/ou modifica-lo sob os termos da Licenca Publica Geral GNU conforme publicada pela Free Software Foundation; Este programa e distribuido na expectativa de que seja util, porem, SEM NENHUMA GARANTIA; nem mesmo a garantia implicita de COMERCIABILIDADE OU ADEQUACAO A UMA FINALIDADE ESPECIFICA. Consulte a Licenca Publica Geral do GNU para mais detalhes. Voce deve ter recebido uma copia da Licenca Publica Geral do GNU junto com este programa; se nao, escreva para a Free Software Foundation, Inc., no endereco 59 Temple Street, Suite 330, Boston, MA 02111-1307 USA. */ // if(typeof(i3GEO) === 'undefined'){ var i3GEO = {}; } /* Classe i3GEO.editorGM Funções de edição vetorial utilizadas pelo editor de regiões do sistema METAESTAT */ i3GEO.editorGM = { iconePonto: function(sel){ if(sel){ return i3GEO.configura.locaplic+"/imagens/google/symbol_middot_y.png"; } else{ return i3GEO.configura.locaplic+"/imagens/google/symbol_middot.png"; } }, /** * Objeto DOM com a imagem de aguarde existente no cabecalho da janela * */ aguarde: "", /** * Guarda o Id do DIV que recebera o conteudo HTML do editor */ iddiv: "", /** * Objeto criado com new google.maps.drawing.DrawingManager */ drawingManager: "", selectedShape: null, /** * guarda o mapeamento entre o codigo da regiao e o codigo do layer adicionado ao mapa */ regioestemas:{}, /** * Guarda o mapeamento entre o codigo da regiao e o codigo do layer adicionado ao mapa */ temasregioes:{}, /** * Guarda os dados descritivos sobre cada regiao obtidos na formacao no combo de escolha de regioes */ descregioes:[], /** * Inicia o editor * * Cria o objeto da API do Google Maps com new google.maps.drawing.DrawingManager * A janela flutuante que recebera os componentes do editor ja deve estar aberta (veja editorlimites.php) * Executa i3GEO.editorGM.html * * @param Id do DIV que recebera o conteudo HTML do editor */ inicia: function(iddiv){ var i,n,ics; //mensagem i3GEO.janela.tempoMsg("Atenção: apenas tabelas no esquema i3geo_metaestat podem ser editadas."); i3GEO.editorGM.iddiv = iddiv; $i(iddiv).innerHTML = i3GEO.editorGM.html(); ics = $i(iddiv).getElementsByTagName("button"); n = ics.length; i3GEO.barraDeBotoes.ativaBotoes(); i3GEO.editorGM.comboRegiaoEditavel(); for(i=0;i' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + ' ' + '
'; //combo para escolher a regiao return ins; }, /** * Atualiza a ferramenta quando a janela flutuante tem seu foco ativado */ ativaFoco: function(){ i3GEO.util.mudaCursor(i3GEO.configura.cursores,"crosshair",i3GEO.Interface.IDMAPA,i3GEO.configura.locaplic); i3GEO.barraDeBotoes.ativaIcone("pan"); i3GEO.editorGM.mudaicone(); i3GEO.Interface.googlemaps.recalcPar(); }, /** * Marca uma figura como selecionada * @param objeto shape que sera marcado */ setSelection: function(shape){ if(shape.setEditable){ shape.setEditable(!shape.editable); } else{ shape.editable = true; shape.setIcon({url: i3GEO.editorGM.iconePonto(true)}); } }, /** * Marca todas as figuras como nao selecionadas * As figuras existentes no mapa sao mantidas na variavel i3GEO.desenho.googlemaps.shapes */ clearSelection: function(){ var i, n = i3GEO.desenho.googlemaps.shapes.length; for(i=0;i 0){ if(naoconfirma === false){ var x = window.confirm("Remove as figuras selecionadas?"); } else{ x = true; } if(x){ for(i=0;i 1){ for(i=1;i < n;i++){ g = geoms[i].toString(); m = new Wkt.Wkt(); m.read(g); w.merge(m); } } return w.write(); }, /** * Funcoes que controlam o processo de obtencao das coordenadas de um componente de uma camada existente no mapa */ capturaPoligonoTema:{ /** * Ativa a operaco de captura definindo o evento que sera executado no onclick do mouse sobre o mapa * O evento executa i3GEO.editorGM.capturaPoligonoTema.captura * @param botao da interface que foi pressionado */ ativa: function(botao){ i3GEO.editorGM.mudaicone(botao); i3GEO.eventos.cliquePerm.desativa(); i3GEO.eventos.adicionaEventos("MOUSECLIQUE",["i3GEO.editorGM.capturaPoligonoTema.captura()"]); i3GEO.util.mudaCursor(i3GEO.configura.cursores,"crosshair",i3GEO.Interface.IDMAPA,i3GEO.configura.locaplic); }, desativa: function(){ }, /** * Realiza a captura de um componente do mapa quando o usuario faz o clique * A captura e feita com classesphp/mapa_controle.php&funcao=identifica3 * O resultado e adicionado ao mapa como um novo objeto shape */ captura: function(){ var temp,tema="",regiao="",p,par, aguarde = $i("i3GEOjanelaEditor_imagemCabecalho"); if(!$i("i3geoCartoRegioesEditaveis")){ i3GEO.eventos.removeEventos("MOUSECLIQUE",["i3GEO.editorGM.capturaPoligonoTema.captura()"]); } else{ temp = function(retorno){ var temp,n,i,WicketWkt, wkt = "", colunaid = i3GEO.editorGM.descregioes["a_"+regiao]["identificador"], valorid = "", colunanome = i3GEO.editorGM.descregioes["a_"+regiao]["colunanomeregiao"], valornome = "", aguarde = $i("i3GEOjanelaEditor_imagemCabecalho"); if(aguarde){ aguarde.style.visibility = "hidden"; } //obtem os dados buscando nos itens que vem da requisicao ao wms temp = retorno.data[0].resultado[0]; if(temp === " "){ i3GEO.janela.tempoMsg("Nada encontrado"); return; } i3GEO.editorGM.mudaicone(); n = temp.length; for(i=0;i"+dados[i].nome_tipo_regiao+""; i3GEO.editorGM.descregioes["a_"+dados[i].codigo_tipo_regiao] = dados[i]; } } ins += ""; if(onde){ onde.innerHTML = ins; } return ins; }; i3GEO.php.listaTipoRegiao(temp,codigo_tipo_regiao); }, /** * Funcao ativada no evento onchange do combo criado com comboRegiaoEditavel * Executa i3GEO.php.mapfileTipoRegiao */ comboRegiaoEditavelOnchange: function(combo){ if(combo.value === ""){ return; } i3GEO.editorGM.editarAtributos.desativa(); var temp = function(retorno){ if(i3GEO.arvoreDeCamadas.pegaTema(retorno.layer) == ""){ i3GEO.php.adtema(i3GEO.atualiza,retorno.mapfile); //guarda o codigo e relaciona com a regiao i3GEO.editorGM.regioestemas["a"+$i("i3geoCartoRegioesEditaveis").value] = retorno.layer; i3GEO.editorGM.temasregioes[retorno.layer] = $i("i3geoCartoRegioesEditaveis").value; } }; i3GEO.php.mapfileTipoRegiao(temp,combo.value); }, /** * Altera as bordas dos icones e desativa eventos * Desativa todos os botoes e ativa o indicado * @param objeto DOM que representa o botao que sera focado */ mudaicone: function(botao){ var c = $i(i3GEO.editorGM.iddiv), ci = c.getElementsByTagName("img"), n = ci.length, i; for(i=0;i 0){ i3GEO.editorGM.deleteSelectedShape(true); } pol = new google.maps.Marker({ position: new google.maps.LatLng(obj.getPosition().ob,obj.getPosition().pb), map: i3GeoMap, icon: { url: i3GEO.editorGM.iconePonto(false) }, clickable: true, zIndex: 1, draggable: true, tema: tema, colunaid: colunaid, valorid: valorid, colunanome: colunanome, valornome: valornome, editable: false }); google.maps.event.addListener(pol, 'click', function() { i3GEO.editorGM.setSelection(pol); }); i3GEO.desenho.googlemaps.shapes.push(pol); return; } }, /** * Salva um poligono no banco de dados */ salvaLimite: { /** * Inicia a ferramenta definindo as funcoes dos botoes * Executa i3GEO.editorGM.salvaLimite.criaJanelaFlutuante */ inicia: function(){ if(i3GEO.login.verificaCookieLogin() === false){ i3GEO.janela.tempoMsg("Você precisa fazer login para usar a opção de salvar no banco de dados"); if(i3GEO.editorGM.selectedShapes().length > 0){ var temp = i3GEO.editorGM.toWKT(i3GEO.editorGM.selectedShapes()[0]); i3GEO.mapa.dialogo.wkt2layer(temp); } return; } var s = i3GEO.editorGM.selectedShapes(), n = s.length, janela = YAHOO.i3GEO.janela.manager.find("salvaLimite"); if(janela){ janela.destroy(); } if(n == 1){ s = s[0]; i3GEO.editorGM.salvaLimite.criaJanelaFlutuante(i3GEO.editorGM.salvaLimite.html( s.colunaid, s.valorid, s.colunanome, s.valornome )); new YAHOO.widget.Button( "i3GEOFmetaestati3GEO.editorGMBotao1", {onclick:{fn: function(){ i3GEO.editorGM.salvaLimite.gravaDados(true); }}} ); new YAHOO.widget.Button( "i3GEOFmetaestati3GEO.editorGMBotao2", {onclick:{fn: function(){ i3GEO.editorGM.salvaLimite.gravaDados(false); }}} ); new YAHOO.widget.Button( "i3GEOFmetaestati3GEO.editorGMBotao3", {onclick:{fn: function(){ i3GEO.editorGM.salvaLimite.excluiPoligono(); }}} ); } else{ i3GEO.janela.tempoMsg("Selecione uma figura"); } }, /** * Monta o HTML para o formulario que permite salvar os dados */ html:function(colunaIdElemento,valorIdElemento,colunaNomeElemento,valorNomeElemento){ var ins = '' + '

Se o valor do código for vazio, será criado um novo elemento. Caso contrário, os valores atualmente registrados serão atualizados.

' + '

Edite os atributos:

' + '' + '' + '' + '' + '

Código:

' + '

' + '

Nome:

' + '

' + '

Escolha a operação desejada:

' + '' + ' ' + '

'; return ins; }, /** * Cria a janela flutuante para receber os componentes da ferramenta * @param html com o conteudo da ferramenta */ criaJanelaFlutuante: function(html){ var titulo,cabecalho,minimiza,janela; cabecalho = function(){}; minimiza = function(){ i3GEO.janela.minimiza("salvaLimite"); }; titulo = "Salva limite   "; janela = i3GEO.janela.cria( "300px", "265px", "", "", "", titulo, "salvaLimite", true, "hd", cabecalho, minimiza ); $i("salvaLimite_corpo").style.backgroundColor = "white"; $i("salvaLimite_corpo").innerHTML = html; YAHOO.util.Event.addListener(janela[0].close, "click", i3GEO.editorGM.mudaicone); }, /** * Aplica a operacao de salvar os dados no banco para o shape selecionado * Executa admin/php/metaestat.php?funcao=mantemDadosRegiao * @param boolean indica se as coordenadas serao salvas tambem */ gravaDados: function(comwkt){ if(i3GEO.login.verificaCookieLogin() === false){ i3GEO.janela.tempoMsg("Você precisa fazer login para usar essa opção"); return; } if(!window.confirm("Grava mesmo os dados?")){ return; } var p,codigo_tipo_regiao = $i("i3geoCartoRegioesEditaveis").value, identificadornovo = $i("inputIdentificadorNovoElemento").value, identificador = $i("inputIdentificadorElemento").value, nome = $i("inputNomeNovoElemento").value, wkt = "", temp = function(retorno){ i3GEO.editorGM.deleteSelectedShape(true); var janela = YAHOO.i3GEO.janela.manager.find("salvaLimite"); if(janela){ janela.destroy(); } i3GEO.Interface.redesenha(); }; if(comwkt === true){ wkt = i3GEO.editorGM.toWKT(i3GEO.editorGM.selectedShapes()[0]); } else{ if(identificadornovo === identificador && $i("inputNomeElemento").value === nome){ i3GEO.janela.tempoMsg("Valores iguais ao original"); return; } } p = i3GEO.configura.locaplic+"/admin/php/metaestat.php?funcao=mantemDadosRegiao&tipo="; cpJSON.call(p,"foo",temp,"&codigo_tipo_regiao="+codigo_tipo_regiao+"&identificadornovo="+identificadornovo+"&identificador="+identificador+"&nome="+nome+"&wkt="+wkt); }, /** * Exclui um registro do banco de dados * Executa admin/php/metaestat.php?funcao=mantemDadosRegiao&tipo=excluir */ excluiPoligono: function(){ if(i3GEO.login.verificaCookieLogin() === false){ i3GEO.janela.tempoMsg("Você precisa fazer login para usar essa opção"); return; } if(!window.confirm("Exclui mesmo o poligono?")){ return; } var codigo_tipo_regiao = $i("i3geoCartoRegioesEditaveis").value, identificador = $i("inputIdentificadorElemento").value, temp = function(retorno){ i3GEO.editorGM.deleteSelectedShape(true); var janela = YAHOO.i3GEO.janela.manager.find("salvaLimite"); if(janela){ janela.destroy(); } i3GEO.Interface.redesenha(); }, p = i3GEO.configura.locaplic+"/admin/php/metaestat.php?funcao=mantemDadosRegiao&tipo=excluir"; cpJSON.call(p,"foo",temp,"&codigo_tipo_regiao="+codigo_tipo_regiao+"&identificador="+identificador); } }, /** *Funcoes que controlam o processo de edicao de atributos de um shape */ editarAtributos: { aliascolunas: "", //guarda os nomes das colunas e seus aliases para permitir a criacao de novos registros x: "", y: "", /** * Ativa a ferramenta * Define os eventos de onclick para abrir formulario quando o usuario clica no mapa * Para cada regiao sao obtidas todas as variaveis cadastradas * Executa i3GEO.editorGM.editarAtributos.criaJanelaFlutuante * Executa i3GEO.editorGM.editarAtributos.comboVariaveis(); */ ativa: function(botao){ if($i("i3geoCartoRegioesEditaveis").value == ""){ i3GEO.janela.tempoMsg("Escolha uma regiao"); return; } i3GEO.editorGM.mudaicone(botao); i3GEO.eventos.adicionaEventos("MOUSECLIQUE",["i3GEO.editorGM.editarAtributos.captura()"]); var janela = YAHOO.i3GEO.janela.manager.find("editaAtributos"); if(janela){ $i("editarAtributosForm").innerHTML = ""; } else{ i3GEO.editorGM.editarAtributos.criaJanelaFlutuante(i3GEO.editorGM.editarAtributos.html()); i3GEO.editorGM.editarAtributos.comboVariaveis(); } }, /** * Fecha a janela de edicao */ desativa: function(){ var janela = YAHOO.i3GEO.janela.manager.find("editaAtributos"); if(janela){ janela.destroy(); } }, criaJanelaFlutuante: function(html){ var janela,titulo,cabecalho,minimiza; cabecalho = function(){}; minimiza = function(){ i3GEO.janela.minimiza("editaAtributos"); }; titulo = "Atributos   "; janela = i3GEO.janela.cria( "250px", "265px", "", "", "", titulo, "editaAtributos", false, "hd", cabecalho, minimiza ); $i("editaAtributos_corpo").style.backgroundColor = "white"; $i("editaAtributos_corpo").innerHTML = html; i3GEO.janela.tempoMsg("Após escolher a medida da variável, clique no mapa para escolher o limite geográfico."); YAHOO.util.Event.addListener(janela[0].close, "click", i3GEO.editorGM.mudaicone); }, /** * Fornece o HTML com os objetos que receberao os componentes da ferramenta * @return html */ html: function(){ var ins = '' + '

' + '

' + '

' + '

' + ''; return ins; }, /** * Monta um combo para escolha de uma variavel que sera editada * Executa i3GEO.php.listaVariavel */ comboVariaveis: function(){ var temp = function(dados){ var i,n = dados.length, ins = ''; ins += '

Escolha uma variável para editar

'; ins += ""; $i("editarAtributosVariaveis").innerHTML = ins; }; i3GEO.php.listaVariavel(temp,"i3geo_metaestat"); }, /** * Monta um combo com as medidas de uma variavel * Executa i3GEO.php.listaMedidaVariavel * @param objeto DOM do tipo select que contem a lista de variaveis */ comboMedidasVariavel: function(comboMedidas){ var temp = function(dados){ var i,n = dados.length, ins = ''; ins += '

Escolha uma medida da variável para editar

'; ins += ""; $i("editarAtributosMedidasVariavel").innerHTML = ins; }; if(comboMedidas.value !== ""){ i3GEO.php.listaMedidaVariavel(comboMedidas.value,temp); } }, /** * Captura os atributos de um elemento do mapa * Executa i3GEO.editorGM.editarAtributos.pegaDados(); */ captura: function(){ if(!YAHOO.i3GEO.janela.manager.find("editaAtributos")){ i3GEO.editorGM.mudaicone(botao); return; } i3GEO.editorGM.editarAtributos.x = objposicaocursor.ddx; i3GEO.editorGM.editarAtributos.y = objposicaocursor.ddy; i3GEO.editorGM.editarAtributos.pegaDados(); }, /** * Obtem os dados de um elemento de uma regiao * Monta o formulario para edicao * Executa admin/php/metaestat.php?funcao=listaAtributosMedidaVariavelXY */ pegaDados: function(){ var p = i3GEO.configura.locaplic+"/admin/php/metaestat.php?funcao=listaAtributosMedidaVariavelXY", codigo_tipo_regiao = $i("i3geoCartoRegioesEditaveis").value, id_medida_variavel = $i("editarAtributosComboMedidas").value, temp = function(retorno){ var atr = retorno.atributos, i = 0, n = atr.dados.length, j = 0, idunico = "", nj = atr.aliascolunas.length, ins = "" + '

Limite geográfico escolhido:

' + '

Nome: ' + retorno.regiao.nomeregiao + '

' + '

Código: ' + retorno.regiao.identificador_regiao + '

' + '' + '

Atributos:

' + '' + ' '; $i("editarAtributosRegiao").innerHTML = ins; ins = ""; //registros for(i=0;iexcluir

"; //colunas for(j=0;j' + '

'; } } } $i("editarAtributosForm").innerHTML = ins; new YAHOO.widget.Button( "editarAtributosAdicionar", {onclick:{fn: function(){ var novoel = document.createElement("div"), ins = "

"; for(j=0;j' + '

'; } } ins + "
"; novoel.innerHTML = ins; $i("editarAtributosForm").appendChild(novoel); }}} ); new YAHOO.widget.Button( "editarAtributosSalvar", {onclick:{fn: function(){ i3GEO.editorGM.editarAtributos.salva(); }}} ); }; cpJSON.call(p,"foo",temp,"&codigo_tipo_regiao="+codigo_tipo_regiao+"&id_medida_variavel="+id_medida_variavel+"&x="+i3GEO.editorGM.editarAtributos.x+"&y="+i3GEO.editorGM.editarAtributos.y); }, //FIXME redesenhar as camadas que sofrerem alteracoes em funcao do salvar ou excluir /** * Exclui o valor de uma medida de variavel para o componente de uma regiao */ excluir: function(id){ if(i3GEO.login.verificaCookieLogin() === false){ i3GEO.janela.tempoMsg("Você precisa fazer login para usar essa opção"); return; } var p = i3GEO.configura.locaplic+"/admin/php/metaestat.php?funcao=excluiAtributosMedidaVariavel", codigo_tipo_regiao = $i("i3geoCartoRegioesEditaveis").value, id_medida_variavel = $i("editarAtributosComboMedidas").value, identificador_regiao = $i("editarAtributosidentificador_regiao").value, temp = function(retorno){ i3GEO.editorGM.editarAtributos.pegaDados(); i3GEO.janela.fechaAguarde("aguardeSalvaAtributos"); }; i3GEO.janela.AGUARDEMODAL = true; i3GEO.janela.abreAguarde("aguardeSalvaAtributos","Salvando..."); i3GEO.janela.AGUARDEMODAL = false; cpJSON.call(p,"foo",temp,"&codigo_tipo_regiao="+codigo_tipo_regiao+"&identificador_regiao="+identificador_regiao+"&id_medida_variavel="+id_medida_variavel+"&id="+id); }, /** * Salva os valores digitados */ salva: function(){ if(i3GEO.login.verificaCookieLogin() === false){ i3GEO.janela.tempoMsg("Você precisa fazer login para usar essa opção"); return; } var container = $i("editarAtributosForm"), divsT = container.getElementsByTagName("div"), n = divsT.length, i = 0, dv = "", inputs = "", codigo_tipo_regiao = $i("i3geoCartoRegioesEditaveis").value, id_medida_variavel = $i("editarAtributosComboMedidas").value, identificador_regiao = $i("editarAtributosidentificador_regiao").value, nj, j, colunas = [], colunasT = [], valores = [], valoresT = [], idsunicosT = [], p = i3GEO.configura.locaplic+"/admin/php/metaestat.php?funcao=salvaAtributosMedidaVariavel", re = new RegExp("idunico_", "g"),//prefixo usado para marcar o id dos elementos input que contem os valores que se quer obter temp = function(retorno){ i3GEO.editorGM.editarAtributos.pegaDados(); i3GEO.editorGM.atualizaCamadasMetaestat(); i3GEO.janela.fechaAguarde("aguardeSalvaAtributos"); }; if(codigo_tipo_regiao == ""){ i3GEO.janela.tempoMsg("Problemas com o codigo da regiao"); return; } if(id_medida_variavel == ""){ i3GEO.janela.tempoMsg("Escolha uma medida"); return; } if(identificador_regiao == ""){ i3GEO.janela.tempoMsg("Problemas com o identificador da regiao"); return; } for(i=0;i" + "Clique no mapa para traçar um polígono novo. Cada clique corresponde a um vértice do polígono. Para encerrar o traçado utilize um duplo clique. Após traçar um novo polígono pode-se selecioná-lo novamente e editar os vértices, se for necessário, ou mesmo apagar o polígono por completo. O novo polígono só será salvo por meio da opção específica para isso." + "" + "Utilize essa opção para capturar os vértices de um polígono existente. O resultado da captura é uma figura que pode ser editada, ou seja, os vértices podem ser modificados de posição ou mesmo removidos. Após editar, salve o novo polígono." + "" + "Após ativar essa opção clique no mapa sobre uma figura existente (que tenha sido capturada ou digtalizada). A figura passará então para o estado de 'selecionada' podendo ser utilizada por outras ferramentas de edição." + "" + "Remove da tela a figura que estiver selecionada. Essa operação não apaga o polígono do banco de dados, apenas remove do modo de edição." + "" + "Salva no banco de dados a figura que estiver selecionada. Essa opção altera apenas os atributos do limite geográfico, não afetando os valores armazenados em cada medida de variável." + "" + "Abre um formulário que permite alterar os valores de uma medida de variável relacionada a uma determinada região. Após abrir o formulário, clique no mapa sobre a região desejada, mas escolha a medida da variável primeiro. Os valores já existentes poderão então ser alterados ou podem ser adicionados novos." + ""; $i("editaAtributosAjuda_corpo").innerHTML = html; } }; //copia do original de pacotes/wicket /*global Wkt, google, document, window, console*/ google.maps.Marker.prototype.type = 'marker'; google.maps.Polyline.prototype.type = 'polyline'; google.maps.Polygon.prototype.type = 'polygon'; google.maps.Rectangle.prototype.type = 'rectangle'; google.maps.Circle.prototype.type = 'circle'; /** @license * * Copyright (C) 2012 K. Arthur Endsley (kaendsle@mtu.edu) * Michigan Tech Research Institute (MTRI) * 3600 Green Court, Suite 100, Ann Arbor, MI, 48105 * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ (function (Wkt) { /** * @augments Wkt.Wkt * A framework-dependent flag, set for each Wkt.Wkt() instance, that indicates * whether or not a closed polygon geometry should be interpreted as a rectangle. */ Wkt.Wkt.prototype.isRectangle = false; /** * @augments Wkt.Wkt * An object of framework-dependent construction methods used to generate * objects belonging to the various geometry classes of the framework. */ Wkt.Wkt.prototype.construct = { /** * Creates the framework's equivalent point geometry object. * @param config {Object} An optional properties hash the object should use * @param component {Object} An optional component to build from * @return {google.maps.Marker} */ point: function (config, component) { var c = component || this.components; config = config || {}; config.position = new google.maps.LatLng(c[0].y, c[0].x); return new google.maps.Marker(config); }, /** * Creates the framework's equivalent multipoint geometry object. * @param config {Object} An optional properties hash the object should use * @return {Array} Array containing multiple google.maps.Marker */ multipoint: function (config) { var i, c, arr; c = this.components; config = config || {}; arr = []; for (i = 0; i < c.length; i += 1) { arr.push(this.construct.point(config, c[i])); } return arr; }, /** * Creates the framework's equivalent linestring geometry object. * @param config {Object} An optional properties hash the object should use * @param component {Object} An optional component to build from * @return {google.maps.Polyline} */ linestring: function (config, component) { var i, c; c = component || this.components; config = config || { editable: false }; config.path = []; for (i = 0; i < c.length; i += 1) { config.path.push(new google.maps.LatLng(c[i].y, c[i].x)); } return new google.maps.Polyline(config); }, /** * Creates the framework's equivalent multilinestring geometry object. * @param config {Object} An optional properties hash the object should use * @return {Array} Array containing multiple google.maps.Polyline instances */ multilinestring: function (config) { var i, c, arr; c = this.components; config = config || { editable: false }; config.path = []; arr = []; for (i = 0; i < c.length; i += 1) { arr.push(this.construct.linestring(config, c[i])); } return arr; }, /** * Creates the framework's equivalent Box or Rectangle geometry object. * @param config {Object} An optional properties hash the object should use * @param component {Object} An optional component to build from * @return {google.maps.Rectangle} */ box: function (config, component) { var c = component || this.components; config = config || {}; config.bounds = new google.maps.LatLngBounds( new google.maps.LatLng(c[0].y, c[0].x), new google.maps.LatLng(c[1].y, c[1].x)); return new google.maps.Rectangle(config); }, /** * Creates the framework's equivalent polygon geometry object. * @param config {Object} An optional properties hash the object should use * @param component {Object} An optional component to build from * @return {google.maps.Polygon} */ polygon: function (config, component) { var j, k, c, rings, verts; c = component || this.components; config = config || { editable: false // Editable geometry off by default }; config.paths = []; rings = []; for (j = 0; j < c.length; j += 1) { // For each ring... verts = []; // NOTE: We iterate to one (1) less than the Array length to skip the last vertex for (k = 0; k < c[j].length - 1; k += 1) { // For each vertex... verts.push(new google.maps.LatLng(c[j][k].y, c[j][k].x)); } // eo for each vertex if (j !== 0) { // Reverse the order of coordinates in inner rings if (config.reverseInnerPolygons == null || config.reverseInnerPolygons) { verts.reverse(); } } rings.push(verts); } // eo for each ring config.paths = config.paths.concat(rings); if (this.isRectangle) { return (function () { var bounds, v=0; bounds = new google.maps.LatLngBounds(); for (v in rings[0]) { // Ought to be only 1 ring in a Rectangle if (rings[0].hasOwnProperty(v)) { bounds.extend(rings[0][v]); } } return new google.maps.Rectangle({ bounds: bounds }); }()); } else { return new google.maps.Polygon(config); } }, /** * Creates the framework's equivalent multipolygon geometry object. * @param config {Object} An optional properties hash the object should use * @return {Array} Array containing multiple google.maps.Polygon */ multipolygon: function (config) { var i, c, arr; c = this.components; config = config || { editable: false }; config.path = []; arr = []; for (i = 0; i < c.length; i += 1) { arr.push(this.construct.polygon(config, c[i])); } return arr; } }; /** * @augments Wkt.Wkt * A framework-dependent deconstruction method used to generate internal * geometric representations from instances of framework geometry. This method * uses object detection to attempt to classify members of framework geometry * classes into the standard WKT types. * @param obj {Object} An instance of one of the framework's geometry classes * @param multiFlag {Boolean} If true, then the deconstructor will be forced to return a MultiGeometry (multipoint, multilinestring or multipolygon) * @return {Object} A hash of the 'type' and 'components' thus derived, plus the WKT string of the feature. */ Wkt.Wkt.prototype.deconstruct = function (obj, multiFlag) { var features, i, j, verts, rings, sign, tmp, response, lat, lng; // Shortcut to signed area function (determines clockwise vs counter-clock) if (google.maps.geometry) { sign = google.maps.geometry.spherical.computeSignedArea; }; // google.maps.LatLng ////////////////////////////////////////////////////// if (obj.constructor === google.maps.LatLng) { response = { type: 'point', components: [{ x: obj.lng(), y: obj.lat() }] }; return response; } // google.maps.Point ////////////////////////////////////////////////////// if (obj.constructor === google.maps.Point) { response = { type: 'point', components: [{ x: obj.x, y: obj.y }] }; return response; } // google.maps.Marker ////////////////////////////////////////////////////// if (obj.constructor === google.maps.Marker) { response = { type: 'point', components: [{ x: obj.getPosition().lng(), y: obj.getPosition().lat() }] }; return response; } // google.maps.Polyline //////////////////////////////////////////////////// if (obj.constructor === google.maps.Polyline) { verts = []; for (i = 0; i < obj.getPath().length; i += 1) { tmp = obj.getPath().getAt(i); verts.push({ x: tmp.lng(), y: tmp.lat() }); } response = { type: 'linestring', components: verts }; return response; } // google.maps.Polygon ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Polygon) { rings = []; if (multiFlag === undefined) { multiFlag = (function () { var areas, l; l = obj.getPaths().length; if (l <= 1) { // Trivial; this is a single polygon return false; } if (l === 2) { // If clockwise*clockwise or counter*counter, i.e. // (-1)*(-1) or (1)*(1), then result would be positive if (sign(obj.getPaths().getAt(0)) * sign(obj.getPaths().getAt(1)) < 0) { return false; // Most likely single polygon with 1 hole } return true; } // Must be longer than 3 polygons at this point... areas = obj.getPaths().getArray().map(function (k) { return sign(k) / Math.abs(sign(k)); // Unit normalization (outputs 1 or -1) }); // If two clockwise or two counter-clockwise rings are found // (at different indices)... if (areas.indexOf(areas[0]) !== areas.lastIndexOf(areas[0])) { multiFlag = true; // Flag for holes in one or more polygons return true; } return false; }()); } for (i = 0; i < obj.getPaths().length; i += 1) { // For each polygon (ring)... tmp = obj.getPaths().getAt(i); verts = []; for (j = 0; j < obj.getPaths().getAt(i).length; j += 1) { // For each vertex... verts.push({ x: tmp.getAt(j).lng(), y: tmp.getAt(j).lat() }); } if (!tmp.getAt(tmp.length - 1).equals(tmp.getAt(0))) { if (i % 2 !== 0) { // In inner rings, coordinates are reversed... verts.unshift({ // Add the first coordinate again for closure x: tmp.getAt(tmp.length - 1).lng(), y: tmp.getAt(tmp.length - 1).lat() }); } else { verts.push({ // Add the first coordinate again for closure x: tmp.getAt(0).lng(), y: tmp.getAt(0).lat() }); } } if (obj.getPaths().length > 1 && i > 0) { // If this and the last ring have the same signs... if (sign(obj.getPaths().getAt(i)) > 0 && sign(obj.getPaths().getAt(i - 1)) > 0 || sign(obj.getPaths().getAt(i)) < 0 && sign(obj.getPaths().getAt(i - 1)) < 0 && !multiFlag) { // ...They must both be inner rings (or both be outer rings, in a multipolygon) verts = [verts]; // Wrap multipolygons once more (collection) } } if (i % 2 !== 0) { // In inner rings, coordinates are reversed... verts.reverse(); } rings.push(verts); } response = { type: (multiFlag) ? 'multipolygon' : 'polygon', components: rings }; return response; } // google.maps.Circle ////////////////////////////////////////////////////// if (obj.constructor === google.maps.Circle) { var point = obj.getCenter(); var radius = obj.getRadius(); verts = []; var d2r = Math.PI / 180; // degrees to radians var r2d = 180 / Math.PI; // radians to degrees radius = radius / 1609; // meters to miles var earthsradius = 3963; // 3963 is the radius of the earth in miles var num_seg = 32; // number of segments used to approximate a circle var rlat = (radius / earthsradius) * r2d; var rlng = rlat / Math.cos(point.lat() * d2r); for (var n = 0; n <= num_seg; n++) { var theta = Math.PI * (n / (num_seg / 2)); lng = point.lng() + (rlng * Math.cos(theta)); // center a + radius x * cos(theta) lat = point.lat() + (rlat * Math.sin(theta)); // center b + radius y * sin(theta) verts.push({ x: lng, y: lat }); } response = { type: 'polygon', components: [verts] }; return response; } // google.maps.LatLngBounds /////////////////////////////////////////////////// if (obj.constructor === google.maps.LatLngBounds) { tmp = obj; verts = []; verts.push({ // NW corner x: tmp.getSouthWest().lng(), y: tmp.getNorthEast().lat() }); verts.push({ // NE corner x: tmp.getNorthEast().lng(), y: tmp.getNorthEast().lat() }); verts.push({ // SE corner x: tmp.getNorthEast().lng(), y: tmp.getSouthWest().lat() }); verts.push({ // SW corner x: tmp.getSouthWest().lng(), y: tmp.getSouthWest().lat() }); verts.push({ // NW corner (again, for closure) x: tmp.getSouthWest().lng(), y: tmp.getNorthEast().lat() }); response = { type: 'polygon', isRectangle: true, components: [verts] }; return response; } // google.maps.Rectangle /////////////////////////////////////////////////// if (obj.constructor === google.maps.Rectangle) { tmp = obj.getBounds(); verts = []; verts.push({ // NW corner x: tmp.getSouthWest().lng(), y: tmp.getNorthEast().lat() }); verts.push({ // NE corner x: tmp.getNorthEast().lng(), y: tmp.getNorthEast().lat() }); verts.push({ // SE corner x: tmp.getNorthEast().lng(), y: tmp.getSouthWest().lat() }); verts.push({ // SW corner x: tmp.getSouthWest().lng(), y: tmp.getSouthWest().lat() }); verts.push({ // NW corner (again, for closure) x: tmp.getSouthWest().lng(), y: tmp.getNorthEast().lat() }); response = { type: 'polygon', isRectangle: true, components: [verts] }; return response; } // google.maps.Data Geometry Types ///////////////////////////////////////////////////// // google.maps.Data.Feature ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.Feature) { return this.deconstruct.call(this, obj.getGeometry()); } // google.maps.Data.Point ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.Point) { //console.log('It is a google.maps.Data.Point'); response = { type: 'point', components: [{ x: obj.get().lng(), y: obj.get().lat() }] }; return response; } // google.maps.Data.LineString ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.LineString) { verts = []; //console.log('It is a google.maps.Data.LineString'); for (i = 0; i < obj.getLength(); i += 1) { vertex = obj.getAt(i); verts.push({ x: vertex.lng(), y: vertex.lat() }); } response = { type: 'linestring', components: verts }; return response; } // google.maps.Data.Polygon ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.Polygon) { var rings = []; //console.log('It is a google.maps.Data.Polygon'); for (i = 0; i < obj.getLength(); i += 1) { // For each ring... ring = obj.getAt(i); var verts = []; for (j = 0; j < ring.getLength(); j += 1) { // For each vertex... vertex = ring.getAt(j); verts.push({ x: vertex.lng(), y: vertex.lat() }); } verts.push({ x: ring.getAt(0).lng(), y: ring.getAt(0).lat() }); rings.push(verts); } response = { type: 'polygon', components: rings }; return response; } // google.maps.Data.MultiPoint ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.MultiPoint) { verts = []; for (i = 0; i < obj.getLength(); i += 1) { vertex = obj.getAt(i); verts.push([{ x: vertex.lng(), y: vertex.lat() }]); } response = { type: 'multipoint', components: verts }; return response; } // google.maps.Data.MultiLineString ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.MultiLineString) { linestrings = []; for (i = 0; i < obj.getLength(); i += 1) { verts = []; var linestring = obj.getAt(i); for (j = 0; j < linestring.getLength(); j += 1) { vertex = linestring.getAt(j); verts.push({ x: vertex.lng(), y: vertex.lat() }); } linestrings.push(verts); } response = { type: 'multilinestring', components: linestrings }; return response; } // google.maps.Data.MultiPolygon ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.MultiPolygon) { var k = 0,polygons = []; //console.log('It is a google.maps.Data.MultiPolygon'); for (k = 0; k < obj.getLength(); k += 1) { // For each multipolygon var polygon = obj.getAt(k); var rings = []; for (i = 0; i < polygon.getLength(); i += 1) { // For each ring... ring = polygon.getAt(i); var verts = []; for (j = 0; j < ring.getLength(); j += 1) { // For each vertex... vertex = ring.getAt(j); verts.push({ x: vertex.lng(), y: vertex.lat() }); } verts.push({ x: ring.getAt(0).lng(), y: ring.getAt(0).lat() }); rings.push(verts); } polygons.push(rings); } response = { type: 'multipolygon', components: polygons }; return response; } // google.maps.Data.GeometryCollection ///////////////////////////////////////////////////// if (obj.constructor === google.maps.Data.GeometryCollection) { var objects = []; for (k = 0; k < obj.getLength(); k += 1) { // For each multipolygon var object = obj.getAt(k); objects.push(this.deconstruct.call(this, object)); } //console.log('It is a google.maps.Data.GeometryCollection', objects); response = { type: 'geometrycollection', components: objects }; return response; } // Array /////////////////////////////////////////////////////////////////// if (Wkt.isArray(obj)) { features = []; for (i = 0; i < obj.length; i += 1) { features.push(this.deconstruct.call(this, obj[i], true)); } response = { type: (function () { var k, type = obj[0].constructor; for (k = 0; k < obj.length; k += 1) { // Check that all items have the same constructor as the first item if (obj[k].constructor !== type) { // If they don't, type is heterogeneous geometry collection return 'geometrycollection'; } } switch (type) { case google.maps.Marker: return 'multipoint'; case google.maps.Polyline: return 'multilinestring'; case google.maps.Polygon: return 'multipolygon'; default: return 'geometrycollection'; } }()), components: (function () { // Pluck the components from each Wkt var i, comps; comps = []; for (i = 0; i < features.length; i += 1) { if (features[i].components) { comps.push(features[i].components); } } return { comps: comps }; }()) }; response.components = response.components.comps; return response; } }; }(Wkt || require('./wicket')));