How to bind a custom geometry

Hello,

I want to bind a custom geometry inside the nodeTemplate.
Since I am using GOJS in combination with leaftlet.js, it is important that the geo location of nodes and links is correct.
I want to bind every cornerpoint of a polygon to a geographic location. Otherwise I would just use the Shape.geometryString . So posted a question on stackoverflow, because I thought I was facing a general problem.
I followed the suggested instructions and I think it is working pretty nice (Thank you Walter!). Take a look at the post javascript - How to get the pixel coordinates from a SVG path String - Stack Overflow

Now that I have the correct geographic coordinates to each point how am I able to display this?.
My approach was to create a new geometry object, that I would pass to the node through a binding. I am creating the geometry with this method

//segments is an array of lat/lng pairs function createGeometryForPolygon(startGeoPos, segments) { var point = map.latLngToContainerPoint(startGeoPos); var geometryObject = new go.Geometry() .add(new go.PathFigure(point.x, point.y));
for (var i = 0; i < segments.length; i++) { var pixPos = map.latLngToContainerPoint(segments[i]); geometryObject.add(new go.PathSegment(go.PathSegment.Line, pixPos.x, pixPos.y)); // close the figure if the last segement has been added if (i == segments.length - 1) { geometryObject.add(new go.PathSegment(go.PathSegment.Line, pixPos.x, pixPos.y).close()); } } return geometryObject; }

So I tried to bind this geometry object in different ways:

new go.Binding("figure", "type", function (d) { if (typeof (d) == "object") { console.log(createFiguresForPolygons(d.startGeo, d.geoCoordinates)); return createFiguresForPolygons(d.startGeo, d.geoCoordinates); } else { return d; } }),

new go.Binding("shape", "geo"),

new go.Binding("geometry", "geo"),

I hope you understand what my problem is.

Best regards!

If you search the samples, you will find many instances of dynamic generation of Geometry based on properties in the node data. Just search for new go.Binding("geometry",.

I assume you want your node template to be something like:

$(go.Node,
  { . . . },
  $(go.Shape, { . . . },
    new go.Binding("geometry", "type", ...))
)

Thank you.
I was not aware of all the examples, looking through helped me a lot.
But I dont know how to get the correct Position of the StartPoint of the geometry, because Geometry.startX Geometry.startY are not what I need, at least thats what I think.
Translating the StartPoint with map.containerPointToLatLng() to Lat/Lng values is not giving me the correct result.
The value of StartX is always 0.
Do I need to add something to the Geometry.startX and Geometry.startY values ?
Here is a screenshot oft Geometry.startX and Geometry.startY

Let’s say that you calculate the points in your geometry path using the document coordinate system, in your case involving converting geographical points to GoJS document coordinates.

Hmmm, looking at your code, I see that you are not calling Diagram.transformViewToDoc.

Anyway, when you’ve finished making the Geometry, you can call Geometry.normalize and then set the Node.position to shift the Node accordingly. For example, look at finishShape in extensions/PolygonDrawingTool.js.

      // assign the position for the whole Part
      var pos = geo.normalize();
      pos.x = viewpt.x - pos.x - shape.strokeWidth / 2;
      pos.y = viewpt.y - pos.y - shape.strokeWidth / 2;
      part.position = pos;
      shape.geometry = geo;

I took a deeper look into PolygonDrawingTool.js and its PolygonDrawingTool.prototype.finishShape = function (), and I tried to adapt it into my code.
Looks like I am still doing something wrong here. Take a look at my code:

var viewpt = diagram.viewportBounds.position; console.log(diagram.viewportBounds); var geo = diagram.findNodeForData(nodeData).elt(0).geometry; // assign the position for the whole Part var pos = geo.normalize(); pos.x = viewpt.x - pos.x - nodeData.strokeWidth / 2; pos.y = viewpt.y - pos.y - nodeData.strokeWidth / 2; console.log(pos);

and this is what I get in the console, which obviously is wrong

The -0.5 is simply nodeData.strokeWidth / 2 which means the other 2 values are 0.

Also the suggested method diagram.transformViewToDoc(pos) does not seem to do anything, but maybe I am just using it wrong.

I don’t understand why it has to be this complicated to get a simple StartPoint.

Basically you always need to transform your geographical coordinates into HTML element coordinates, and then those GoJS viewport coordinates into GoJS document coordinates, and then those coordinates into the Shape’s local coordinates.

Leaflet’s `latLngtoContainerPoint** does the first step. Diagram.transformViewToDoc** does the second step. Normalizing the Geometry and setting the Node’s position does the third step.

Note that the normalization of the third step was not necessary when not involving Shape and Geometry. The GoJS Leaflet sample only positions the Nodes, which is always in document coordinates;.

If I get time much later today I can try to create a sample for you.

OK, I’ve added the following node template to the GoJS samples/leaflet.html sample:

    myDiagram.nodeTemplateMap.add("Poly",
      $(go.Node,
        { locationSpot: go.Spot.Center },
        new go.Binding("location", "latlong", computeMiddle),
        $(go.Shape, { fill: "transparent" },
          new go.Binding("geometry", "latlong", computeGeo))
      ));

    function computeMiddle(v) {
      var minlng = Infinity;
      var maxlng = -Infinity;
      var minlat = Infinity;
      var maxlat = -Infinity;
      v.forEach(function(a) {
        minlng = Math.min(minlng, a[1]);
        maxlng = Math.max(maxlng, a[1]);
        minlat = Math.min(minlat, a[0]);
        maxlat = Math.max(maxlat, a[0]);
      })
      var mappoint = myLeafletMap.latLngToContainerPoint({ lat: (minlat + maxlat) / 2, lng: (minlng + maxlng) / 2 });
      return myDiagram.transformViewToDoc(new go.Point(mappoint.x, mappoint.y));
    }

    function computeGeo(v) {
      var fig = null;
      v.forEach(function(a) {
        var mappoint = myLeafletMap.latLngToContainerPoint({ lat: a[0], lng: a[1] });
        var docpoint = myDiagram.transformViewToDoc(new go.Point(mappoint.x, mappoint.y));
        if (fig === null) {
          fig = new go.PathFigure(docpoint.x, docpoint.y, );
        } else {
          fig.add(new go.PathSegment(go.PathSegment.Line, docpoint.x, docpoint.y));
        }
      });
      fig.segments.last().close();
      var geo = new go.Geometry().add(fig);
      geo.normalize();
      return geo;
    }

and I added this one node data object to the model:

{ category: "Poly", latlong: [[49.492755, 0.125278], [51.449541, -2.581118], [48.387778, -4.479921]] },

The result:

And because I named the Array of lat,lng numbers “latlong”, they automatically get recomputed as the user zooms in or out. That’s because of the call to:

myDiagram.updateAllTargetBindings("latlong");

If I had used a different property name I could have added another call to Diagram.updateAllTargetBindings, although that would have been less efficient.