Style building based on proximity to nearest pin

The tile styling demo in Sandbox has a great feature to Color by Distance to Selected Location:

The code from this function is:
// Color the buildings based on their distance from a selected central location
function colorByDistanceToCoordinate(pickedLatitude, pickedLongitude) {
osmBuildingsTileset.style = new Cesium.Cesium3DTileStyle({
defines: {
distance:
“distance(vec2(${feature[‘cesium#longitude’]}, ${feature[‘cesium#latitude’]}), vec2(” +
pickedLongitude +
“,” +
pickedLatitude +
“))”,
},
color: {
conditions: [
[“${distance} > 0.014”, “color(‘blue’)”],
[“${distance} > 0.010”, “color(‘green’)”],
[“${distance} > 0.006”, “color(‘yellow’)”],
[“${distance} > 0.0001”, “color(‘red’)”],
[“true”, “color(‘white’)”],
],
},
});
}

I would like to have buildings styled based on multiple locations (pins), not just one picked location. For example, assume there are three pins marked on the map. Each building then does a distance check against each pin, and the distance that is returned is the nearest pin (shortest distance). If there are three pins, then in the function above, buildings that are > 0.0001 from each pin are colored red.

My question… is there a Cesium function that can check the distance of the nearest pin to each building, such that the ${distance} that is passed to conditions to styling is always the closest Pin?

Hiya,

Nothing out of the box as such (apart from; see below), and it heavily depends on if you got the info required in the model. If you pop long/lat/height into the properties of your model, things become a bit easier. If you can’t, you can always traverse all the tiles in the tileset, as each of them have a bounding circle which you can use, depending what is in the tile in question (sometimes it’s one building, other times it might be many). The resolution of this kind of meta data will be the key to what you’re trying to do.

But if you do have that info, the rest is “easy”; distance between pins are done through simple math or the Cesium.Cartesian3.distance, including direct line vs. over the globe ellipse. More on all of that here;

After that it’s all about writing those style rules around sitance between the pins and your coords / bounding coords, and update accordingly. If the pins move, update the style on the move, or if they move all the time, pop them into a cycle every X seconds. Let me know if you want some specifics about something in this bucket of stuff.

Cheers,

Alex

Hi Alex,

I do have the lat/long of the pins in the model. And thanks for sharing the link to the calculateDistance() for these points. But I’m still struggling with the styling part.

I can calculate distance between a tile and a pin. I can even loop through the pins to find the shortest distance, outside of this styling function. But using Cesium.Cesium3DTileStyle (using example function in Sandbox shared above), it sets a distance and then styling based on this distance. So, if I loop through multiple pins before calling this function (a for loop), I get the color conditions for the last pin in my loop. And when I try to add logic inside the function in distance, I don’t have the ability to loop through pins to find the nearest one.

You mentioned sharing other examples around style rules between pins. If you have other examples to point me towards, it would be greatly appreciated.

Hiya,

Bit hard to quite capture what you’re doing, is it possible to try to make us a Sandcastle example of what you’re doing? Also, are the pins moving in real-time, or at intervals, or is it a static scene? Initializing a colorizing ruleset takes a performance hit and is not suited for real-time, for example. But share some code, it makes it easier to see what you’re doing and try to offer some help.

Cheers,

Alex

Hi Alex,

Pins can be moved in my scenario, but I have that part handled already and capture an array of lat/long for each pin when they move. It’s the styling part that I’m stuck on, so I’ll try to explain it in more detail (but a simplified version of the code):

Imagine two pins placed on the map (my scenario has more than two, but this should do fine for the example). A lat/long for each pin is set elsewhere (latitude_pinX, longitude_pinY). This part is straightforward and works.

// Location of Pin 1 

var pin1 = Cesium.when(
pinBuilder.fromMakiIconId(“marker”, Cesium.Color.GREEN, 48),
function (canvas) {
return viewer.entities.add({
name: “pin1”,
position: Cesium.Cartesian3.fromDegrees(longitude_pin1, latitude_pin1),
billboard: {
image: canvas.toDataURL(),
},
});
}
);

// Location of Pin 2
var pin2 = Cesium.when(
pinBuilder.fromMakiIconId(“marker”, Cesium.Color.RED, 48),
function (canvas) {
return viewer.entities.add({
name: “pin2”,
position: Cesium.Cartesian3.fromDegrees(longitude_pin2, latitude_pin2),
billboard: {
image: canvas.toDataURL(),
},
});
}
);

But when I try to style the tiles near the pins, I can’t calculate the distance from each pin for styling. Everything that I’ve tried so far either doesn’t work, or only does styling from one of the pins.

The following code is borrowed from the aforementioned Sandcastle demo, but I’ve simplified it and commented out the distance section. In short, I’m trying to have a color radius around each pin. Tiles nearby each pin should be green, then turning yellow as it gets further from each pin. In this code example, there should be two circles of green on the map (because of two pins).

osmBuildingsTileset.style = new Cesium.Cesium3DTileStyle({
defines: {
distance:
// calculate closest distance from vec2(${feature[‘cesium#longitude’]}, ${feature[‘cesium#latitude’]})
// to the nearest pin such that the styling will be color green near each pin ( < 0.002 )
},
color: {
conditions: [
["${distance} > 0.002", “color(‘yellow’)”],
["${distance} > 0.0001", “color(‘green’)”],
[“true”, “color(‘white’)”],
],
},
});

Hiya,

Thanks for that, although it’s still a bit hard to fathom what you’re doing. For example;

["${distance} > 0.002", “color(‘yellow’)”],

That’s a reference to the variable injected called ‘distance’, but that section is commented out, and also, in your case, is an array rather than a static calculated pair. The colorizing rules is for the whole model, but it sounds like you’re after different colorizing rules per tile? So, if you put a pin near a building, and there’s another pin around, each colorizing rule should find some average based on the number of pins?

Frankly, if that’s the case, I’m not sure this is the solution you’re after, I might look into creating a heatmap (and look that up in the Cesium docs; there’s some references to it) from your pins, and look at styling a material with the heatmap over the buildings in the camera view (even drape a materialized polygon calculated heatmap over an area calculated from your pins [ie. your pins as a polygon, and expand the polygon with a certain radius]).

Not sure if you’ve got a world model or only care about more local cases, but the short is that the colorizing rules works well for simple unified data, but not at all for complex rules. Maybe you could draw a sketch of your end result instead in some drawing package? Maybe that will clarify it a bit better.

Cheers,

Alex

Okay, I’ll look at a different solution like a heat map (unless anyone else reading this thread has another idea). If I can find a solution, I’ll post back to this thread so that others can see it.

No worries. But I’d still encourage you to draw up a sketch of it, as I might be misunderstanding what you’re after.

Cheers,

Alex

Sure, here’s a quick sketch of what I have in mind. This is an example of two pins and two colors. Keep in mind that the green and yellow circles represent the area that the buildings would be colored green and yellow (not actual circles drawn on the map).

From each pin (two in this example), all buildings within certain radius are colored green. Beyond that radius, colored yellow.

Yeah, colorizing rules are probably not for you. But, how about a simpler solution? Why not simply create those two circles per pin with the colors you want, and make them clamp to ground; that will color the buildings with that color (using vertical draping), and no need to create crazy rules and complicated scenarios. Should be very fast as well. Outer circle at opacity 0.3 and inner at 0.6 would probably do the trick.

Cheers,

Alex

Great idea. Glad I posted here in the forum to get an expert’s opinion. Thanks much Alex!

Cool. I’ve captured a real Cesium example (from a big app, so nothing I can share in terms of code easily) of what it might look like as the entity drapes over a model (like buildings, etc.), just a red pin with a yellow opaque circle. Here I’m using a polygon, but any shape would do the same;

I’d suggest you make a Sandcastle example as a first stab, then we all can chip in from there once you’ve got the basics in place.

Cheers,

Alex

1 Like

Hi Alexander, how can i make the pin follow the model (tileset)?