Adiciona um botão no WME para simplificar a tarefa de alinhar segmentos de rua em uma reta.
目前為
// ==UserScript==
// @name WME Alinhador e simplificador para segmentos de ruas retas
// @version 0.90
// @description Adiciona um botão no WME para simplificar a tarefa de alinhar segmentos de rua em uma reta.
// @author jonny3D, Rômulo Nunes
// @include https://*.waze.com/*editor*
// @include https://editor-beta.waze.com/*
// @include https://beta.waze.com/*
// @exclude https://www.waze.com/user/*editor/*
// @exclude https://www.waze.com/*/user/*editor/*
// @grant none
// @namespace https://greasyfork.org/users/12985
// ==/UserScript==
setTimeout(SimplifyStreetGeometry, 1999);
function SimplifyStreetGeometry() {
UpdateSegmentGeometry = require("Waze/Action/UpdateSegmentGeometry");
MoveNode = require("Waze/Action/MoveNode");
AddNode = require("Waze/Action/AddNode");
MultiAction = require("Waze/Action/MultiAction");
// multiaction.setModel(Waze.model);
Waze.selectionManager.events.register("selectionchanged", null, insertSimplifyStreetGeometryButtons);
function insertSimplifyStreetGeometryButtons() {
$('.edit-restrictions').after('<button id="SimplifyStreetGeometry" class="btn btn-primary"> Alinhar Rua </button>');
}
$('#sidebar').on('click', '#SimplifyStreetGeometry', function(event) {
event.preventDefault();
DoSimplifyStreetGeometry();
});
function DoSimplifyStreetGeometry() {
console.log("WME-ASSRR ========================================================================================" );
console.log("WME-ASSRR I N I C I O D O A L G O R I T M O D E A L I N H A M E N T O" );
console.log("WME-ASSRR ========================================================================================" );
if (Waze.selectionManager.selectedItems.length > 0) {
var T1, T2,
t;
// A = 0.0,
// B = 0.0,
// C = 0.0;
var correct = true;
var nodestrabalhados_max = 30; //numero máximo de nós que o algoritmo suporta.
var nodestrabalhados = [nodestrabalhados_max-1]; //guarda os nós já trabalhados para o alinhamento para verificar se o segmento está invertido no meio dos outros A -B ou B- A
var nodestrabalhados_i = 0;
for (var e0=0; e0 <= nodestrabalhados_max-1; e0++) {nodestrabalhados[e0] = 0;} //iniciando a memória para os nós visitados.
console.log("WME-ASSRR: Limpeza da memória OK");
// define a linha de alinhamento
if (Waze.selectionManager.selectedItems.length > 0) {
console.log("WME-ASSRR: Inicio do cálculo da reta principal... (Percorrendo os segmentos)");
for (var e = 0; e < Waze.selectionManager.selectedItems.length; e++) {
var segment = Waze.selectionManager.selectedItems[e];
if (segment.model.type != "segment")
continue;
var geometry = segment.model.geometry;
// determine a fórmula da linha inclinada
if (geometry.components.length > 1)
{
//Existem duas formas de pegar um nó. A primeira através das extremidades do segmento. A segunda pegando os nós "from" e "to" do segmento.
var A1 = geometry.components[0].clone();
var A2 = geometry.components[geometry.components.length - 1].clone();
var nodeID_A = segment.model.attributes.fromNodeID;
var nodeID_B = segment.model.attributes.toNodeID;
//var node_A = Waze.model.nodes.get(nodeID_A);
//var node_B = Waze.model.nodes.get(nodeID_B);
var flagJaIncluidoA = 0;
var flagJaIncluidoB = 0;
for (e0 = 0; e0 <= nodestrabalhados_i; e0++)
{
console.log("Contador e0: " + e0);
if (nodestrabalhados[e0] == nodeID_A)
{
flagJaIncluidoA = 1 ;
continue;
}
else if (nodestrabalhados[e0] == nodeID_B)
{
flagJaIncluidoB = 1 ;
continue;
}
}
if (flagJaIncluidoA == 0)
{
nodestrabalhados[nodestrabalhados_i++] = nodeID_A;
}
if (flagJaIncluidoB == 0)
{
nodestrabalhados[nodestrabalhados_i++] = nodeID_B;
}
console.log("IDs dos Nós da reta principal: " + nodestrabalhados );
console.log("WME-ASSRR========================================================================================" );
console.log("WME-ASSRR: segmento #" + (e + 1) + " A1(" + A1.x + "; " + A1.y + ") - A2(" + A2.x + "; " + A2.y + ")");
//console.log("WME-ASSRR: segmento #" + (e + 1) + " A1(" + node_A.x + "; " + node_A.y + ") - A2(" + node_B.x + "; " + node_A.y + ")");
// Determinando a inclinação do segmento de seleção.
// Preciso saber como o segmento está no plano 2D, pra determinar quais extremidades pegar.
// não dá pra pegar qualquer nó fixo porque os segmentos podem estar juntos e invertidos B --> A com um A --> B.
var dX = GetDeltaDirect(A1.x, A2.x); // 0: a reta está na vertical / 1: o ponto A está à esquerda de B. / -1: o ponto A está à direita de B (invertido))
var dY = GetDeltaDirect(A1.y, A2.y); // 0: a reta está na horizontal / 1: o ponto A está abaixo de B. / -1: o ponto A está acima de B (invertido))
if (dX < 0) // Se os Pontos A e B estiverem invertidos no eixo X, Desinverto, de forma que A1 tenha o menor X (mais à esquerda).
{
t = A1.x;
A1.x = A2.x;
A2.x = t;
t = A1.y;
A1.y = A2.y;
A2.y = t;
dX = GetDeltaDirect(A1.x, A2.x); //pra atualizar o valor dX e dY caso tenha invertido.
dY = GetDeltaDirect(A1.y, A2.y);
console.log("WME-ASSRR: Houve ajuste no segmento #" + (e + 1)) ;
console.log("WME-ASSRR: Ficou agora: A1 (" + A1.x + "; " + A1.y + ") - A2 (" + A2.x + "; " + A2.y + "), dX=" + dX + ", dY=" + dY);
}else if( dX == 0) // Se a reta estiver na vertical (muito raro), verifico Dx, de forma que fique de baixo pra cima. (poderia ter incluindo no if anterior um "ou (( dX == 0) & ( dY < 0))", mas não sei o comando "ou" nessa linguagem. kkkkk
{
if (dY < 0)
{
t = A1.x;
A1.x = A2.x;
A2.x = t;
t = A1.y;
A1.y = A2.y;
A2.y = t;
dX = GetDeltaDirect(A1.x, A2.x); //pra atualizar o valor dX e dY caso tenha invertido.
dY = GetDeltaDirect(A1.y, A2.y);
}
}
if (e === 0) //primeiro passo da iteração, apenas guardo os dois pontos.
{
T1 = A1.clone();
T2 = A2.clone();
} else { // nos seguintes, apenas expando o ponto final de X, unindo-o na frente ou atrás.
if (A1.x < T1.x)
{
T1.x = A1.x;
T1.y = A1.y;
}
if (A2.x > T2.x)
{
T2.x = A2.x;
T2.y = A2.y;
}
}
console.log("WME-ASSRR: Reta parcial para o cálculo do alinhamento: T1 (" + T1.x + "; " + T1.y + ") , T2 (" + T2.x + "; " + T2.y + ")");
}
}
// Dada a reta calculada acima (ponto inicial da rua e ponto fina) da rua que se quer alinar,
// precisamos calcular os novos pontos de interseção com a retas das ruas que se conectam a rua alinhada.
// o alinhamento se dá pelos novos pontos de interseção.
// Essa é a função da primeira reta.
//A = T2.y - T1.y;
//B = T1.x - T2.x;
//C = T2.x * T1.y - T1.x * T2.y;
console.log("WME-ASSRR: Reta final calculada: T1 (" + T1.x + ";" + T1.y + ") - T2 (" + T2.x + ";" + T2.y + ")");
console.log("WME-ASSRR: =====================================================================================");
// desenhe uma linha de controle
/*var seg1geo = segment.model.geometry;
if (seg1geo.components.length > 2)
seg1geo.components.splice(1, seg1geo.components.length - 2);
seg1geo.comments[0].x = T1.x;
seg1geo.comments[0].y = T1.y;
seg1geo.comments[1].x = T2.x;
seg1geo.comments[1].y = T2.y;
var newseg1 = new Waze.Feature.Vector.Segment(seg1geo);
newseg1.attributes.fromNodeID = null;
newseg1.attributes.toNodeID = null;
Waze.model.actionManager.add(new Waze.Action.AddSegment(newseg1));
*/
} else
correct = false;
if (correct) // correct
{
console.log("");
// remover nós desnecessários (simplificando segmentos)
for (var e2 = 0; e2 < Waze.selectionManager.selectedItems.length; e2++)
{
var segment2 = Waze.selectionManager.selectedItems[e2];
var model = segment2.model;
if (model.type != "segment")
continue;
console.log("" );
console.log("WME-ASSRR Percorrendo seleção para simplificar... Segmento #"+ (e2 + 1) + " =======================================" );
var newGeometry = model.geometry.clone();
var flagSimpled = false;
if (newGeometry.components.length > 2)
{
newGeometry.components.splice(1, newGeometry.components.length - 2);
flagSimpled = true;
}
if (flagSimpled)
Waze.model.actionManager.add(new UpdateSegmentGeometry(model, model.geometry, newGeometry));
}
console.log("");
console.log("WME-ASSRR: Início da etapa para alinhar segmentos...");
for (e2 = 0; e2 < nodestrabalhados_i; e2++ ) //percorrer toda a seleção
{
// trabalhar com o nó para encontrar o nó vizinho da reta adjacente.
//decidir qual nó do segmento trabalhar, pegando o fromNodeID ou toNodeID, conforme o caso.
console.log("" );
console.log("WME-ASSRR Percorrendo seleção para alinhar... Segmento #"+ (e2 + 1) + " =======================================" );
var node = Waze.model.nodes.get(nodestrabalhados[e2]);
var nodeGeo = node.geometry.clone();
console.log("WME-ASSRR ID Atual: " + nodestrabalhados[e2] );
console.log("WME-ASSRR Nó: " + nodeGeo );
var node2 = node;
var nodeGeo2 = node2.geometry.clone();
var pontoparaajuste = nodeGeo.clone(); //inicializando a variável com o mesmo ponto da origem da reta
console.log("");
console.log("WME-ASSRR: PERCORRENDO SEGMENTOS QUE PERTENCEM AO MESMO NÓ");
console.log("WME-ASSRR: Num de segmentos: " + node2.attributes.segIDs.length); //número de segmentos do nó.
console.log("WME-ASSRR: Nó de intersecção: " + nodeGeo); //nó usado para busca
var encontrouRetaADJ = 0;
for(var j=0;j<node2.attributes.segIDs.length;j++) //percorre todos os segmentos que pertencem ao nó. Devemos parar quando encontrar um nó adj.
{
if (encontrouRetaADJ == 1) continue;
console.log("");
console.log("WME-ASSRR: ==== Segmento #" + (j + 1) );
var segID=node2.attributes.segIDs[j];
var segADJ=Waze.model.segments.get(segID);
if(segADJ==undefined)continue;
var segADJgeo = segADJ.geometry.clone();
console.log("WME-ASSRR: ==== Segmento da selecao " + segADJgeo );
var segADJ_A = segADJgeo.components[0].clone();
var segADJ_B = segADJgeo.components[1].clone();
var segADJ_dif = segADJgeo.components[0].clone();
console.log("WME-ASSRR: ======== Nó A: " + segADJ_A);
console.log("WME-ASSRR: ======== Nó B: " + segADJ_B);
if ((segADJ_A.x == nodeGeo.x) & (segADJ_A.y == nodeGeo.y)) //comparando para pegar o outro nó, sem ser o de interseção.
{
console.log("WME-ASSRR: ============ Imprimindo nó diferente (B): " + segADJ_B);
segADJ_dif = segADJ_B;
}
else
{
console.log("WME-ASSRR: ============ Imprimindo nó diferente (A): " + segADJ_A);
}
// verificar se o nó de fato não pertence ao segmento a ser alinhado, mas que seja da reta adjacente.
// se o nó segADJ_dif não pertencer a algum outro ponto da seleção, então é de um segmento adj.
console.log("");
console.log("WME-ASSRR: ==== PERCORRER O VETOR DE SELECAO PARA VERIFICAR SE NÓ É DE RETA ADJ");
var encontrounaselecao = 0;
for (var e3 = 0; e3 < nodestrabalhados_i; e3++ ) //percorrer toda a seleção
{
console.log("" );
console.log("WME-ASSRR: WME-ASSRR Percorrendo seleção para alinhar... Segmento #"+ (e3 + 1) + " =======================================" );
var node_e3 = Waze.model.nodes.get(nodestrabalhados[e3]);
var nodeGeo_e3 = node_e3.geometry.clone();
console.log("WME-ASSRR: ID Atual: " + nodestrabalhados[e3] );
console.log("WME-ASSRR: Nó: " + nodeGeo_e3 );
if ((nodeGeo_e3.x == segADJ_dif.x) & (nodeGeo_e3.y == segADJ_dif.y))
{
encontrounaselecao = 1;
continue;
}
}
if (encontrounaselecao == 0)
{
console.log("WME-ASSRR: ============ Encontrado (Seg é de reta ADJ)");
encontrouRetaADJ = 1;
pontoparaajuste = segADJ_dif.clone();
}
else
{
console.log("WME-ASSRR: ============ Não encontrado (seg é da reta principal)");
}
}
//Refs. bibliográficas sobre a função de interseção:
//1. http://www.inf.pucrs.br/~pinho/CG/Aulas/OpenGL/Interseccao/CalcIntersec.html
//2. John Vince, Geometry for Computer Graphics, Springer, 2005
//3. http://www.dpi.inpe.br/livros/bdados/cap2.pdf (Melhor pra entender )
if (encontrouRetaADJ == 1)
{
// Apresentando a reta ADJ encontrada para o calculo do ponto de intersecção.
console.log("WME-ASSRR: Reta ADJ encontrada:");
var S1 = nodeGeo.clone();
var S2 = pontoparaajuste.clone();
console.log("WME-ASSRR: Origem: " + S1);
console.log("WME-ASSRR: Fim: " + S2);
//Temos as retas dadas pelos pontos T1 e T2 e S1 e S2.
// k l m n
var determinante = ((S2.x - S1.x) * (T2.y - T1.y)) - (( S2.y - S1.y) * (T2.x - T1.x));
if (determinante == 0) //retas paralelas
{
console.log("WME-ASSRR: Retas PARALELAS (chamar próximo)");
continue; //pegar próxima etapa no lop
}
else
{
console.log("WME-ASSRR: Determinante: " + determinante);
var u = (((S2.x - S1.x) * (S1.y - T1.y)) - ((S2.y - S1.y) * (S1.x - T1.x)))/ determinante;
var v= (((T2.x - T1.x) * (S1.y - T1.y)) - ((T2.y - T1.y) * (S1.x - T1.x)))/ determinante;
nodeGeo2.x = T1.x + u * (T2.x - T1.x ) ;
nodeGeo2.y = T1.y + u * (T2.y - T1.y ) ;
}
console.log("WME-ASSRR: Nó atualizado (interseção das retas): " + nodeGeo2);
//nodeGeo2.calculateBounds();
// Ajustando os segmentos adjacentes para o novo ponto do nó (novas coordenadas)
var connectedSegObjs = {}; //guardando os segmentos que pertencem ao Nó pra usar no final para mover o nó.
var connectedSegObjs_copia = {};
var emptyObj = {};
var multiaction = new MultiAction();
multiaction.setModel(Waze.model);
for (var k = 0; k < node2.attributes.segIDs.length; k++)
{
var segid = node2.attributes.segIDs[k];
connectedSegObjs[segid] = node2.model.segments.get(segid).geometry.clone();
connectedSegObjs_copia[segid] = connectedSegObjs[segid];
console.log(" " );
console.log("WME-ASSRR: Segmentos conectados ao Nó atual. ID " + segid );
var connectedSegObjs_A = connectedSegObjs[segid].components[0].clone();
console.log("WME-ASSRR: Seg A para ajuste: " + connectedSegObjs_A);
var connectedSegObjs_B = connectedSegObjs[segid].components[1].clone();
console.log("WME-ASSRR: Seg B para ajuste: " + connectedSegObjs_B);
if ((nodeGeo.x ==connectedSegObjs_A.x) & ( nodeGeo.y == connectedSegObjs_A.y)) //ajuste manual das retas conectadas.
{
connectedSegObjs[segid].components[0].x = nodeGeo2.x;
connectedSegObjs[segid].components[0].y = nodeGeo2.y;
}
else if ((nodeGeo.x ==connectedSegObjs_B.x) & ( nodeGeo.y == connectedSegObjs_B.y))
{
connectedSegObjs[segid].components[1].x = nodeGeo2.x;
connectedSegObjs[segid].components[1].y = nodeGeo2.y;
}
console.log("WME-ASSRR: Ajustado A para : " + connectedSegObjs[segid].components[0]);
console.log("WME-ASSRR: Ajustado B para : " + connectedSegObjs[segid].components[1]);
multiaction.doSubAction(new UpdateSegmentGeometry(node2.model.segments.get(segid), connectedSegObjs_copia[segid] , connectedSegObjs[segid]));
}
//console.log("flag1");
//Waze.model.actionManager.add(new MoveNode(node, node.geometry, nodeGeo2));
//Waze.model.actionManager.add(new MoveNode(node, node.geometry, nodeGeo2, connectedSegObjs, emptyObj));
nodeGeo2.calculateBounds();
multiaction.doSubAction(new MoveNode(node, node.geometry, nodeGeo2, connectedSegObjs, emptyObj));
Waze.model.actionManager.add(multiaction);
console.log("WME-ASSRR: Final do ajuste do Nó");
console.log("=====================================================================================");
}
}
}
}
}
// calcular o novo ponto perpendicular de um ponto com uma linha reta inclinada
//Opção 1
/*function GetIntersectCoord(A, B, C, W) {
// fórmulas aqui: https://otvet.mail.ru/question/36001356
var r = [2];
r[0] = (x1 * y2 * y2 - 2 * x1 * y1 * y2 + x1 * y1 * y1 - x2 * y1 * y2 + x2 * y1 * y1 + x1 * y1 * y2 - x1 * y1 * y1 + 2 * x1 * x2 * x3 + x3 * x2 * x2 + x3 * x1 * x1 + x2 * y2 * y3 - x2 * y1 * y3 - x1 * y2 * y3 + x1 * y1 * y3) / (y2 * y2 - 2 * y1 * y2 + y1 * y1 - 2 * x1 * x2 + x2 * x2 + x1 * x1);
r[1] = (r[0] - x1) * (y2 - y1) / (x2 - x1) + y1;
return r;*/
//Opção 2
/*function GetIntersectCoord(A, B, C, W) {
// a segunda opção é mais simples: http://rsdn.ru/forum/alg/2589531.hot
var r = [2];
r[1] = -1.0 * (C * B - A * W) / (A * A + B * B);
r[0] = (-r[1] * (B + A) - C + W) / (A - B);
return r; */
//Opção 3
function GetIntersectCoord(A1, B1, C1, A2, B2, C2, W) {
//x:=(b1*c2 - b2*c1)/denom;
//y:=(a2*c1 - a1*c2)/denom;
var r = [2];
r[0] = 1;
r[1] = 1;
if (W != 0){
r[0] = (B1 * C2 - B2 * C1)/W;
r[1] = (A2 * C1 - A1 * C2)/W;
}
return r;
}
// define os guias
function GetDeltaDirect(A, B) {
var d = 0.0;
if (A < B)
d = 1.0;
else if (A > B)
d = -1.0;
return d;
}
}