WME Alinhador e simplificador para segmentos de ruas retas

Adiciona um botão no WME para simplificar a tarefa de alinhar segmentos de rua em uma reta.

目前為 2018-03-04 提交的版本,檢視 最新版本

您需要先安裝使用者腳本管理器擴展,如 TampermonkeyGreasemonkeyViolentmonkey 之後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyViolentmonkey 後才能安裝該腳本。

您需要先安裝使用者腳本管理器擴充功能,如 TampermonkeyUserscripts 後才能安裝該腳本。

你需要先安裝一款使用者腳本管理器擴展,比如 Tampermonkey,才能安裝此腳本

您需要先安裝使用者腳本管理器擴充功能後才能安裝該腳本。

(我已經安裝了使用者腳本管理器,讓我安裝!)

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展,比如 Stylus,才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

你需要先安裝一款使用者樣式管理器擴展後才能安裝此樣式

(我已經安裝了使用者樣式管理器,讓我安裝!)

// ==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;
	}
}