Smoothly rotate icons via interpolate on MapboxGL - mapbox-gl-js

I'm trying to animate the rotation of my map markers when their values change using MapboxGL's data driven styling and the interpolate expression. Here is the relevant part of the layer config:
{
layout: {
'icon-rotate': ['interpolate', ['linear'], ['number', ['get', 'winddir'], 0], -180,-180, -90,-90, 0,0, 90, 90, 180, 180]
}
}
The winddir property will be a value between -180 & 180.
The markers appear on the map rotated correctly. However, when they change, they "snap" to the next position. I'm thinking I'm not using the "stops" correctly. Here are the interpolate docs.

As the documentation says:
Layout property. Optional number. Units in degrees. Defaults to 0. Requires icon-image. Supports interpolate expressions.
It does not include the magic term "transitionable". So, animation transitions are not applied to icon-rotate.

Related

SCNNode Rotation Multiple Axes

This Question was posted, but never answered.
Similar to This Question, I am trying to understand SCNNode.rotation as a 4D vector. The prior question utilizes an example that only manipulates 1 axis, i.e.,
SCNNode.rotation = (0, 0, 1, degToRad(45)) //Rotate about z-axis by 45 degrees
which makes sense; however, what if I wanted to rotate the X axis by 20 degrees, Y axis by 45 degrees and then Z axis by 78 degrees?
SCNNode.rotation = ??
I would provide code I've tried, but I don't understand conceptually the notion of a 4D rotation vector.
Every node just has a transform with 4x4 matrix. So all the rotation operations are reflecting in the changing the transform.
In this case, if you change either of rotation, eulerAngles and orientation, you are supposed to get same value.
If rotating about three axises, I suggested using eulerAngles.
node.eulerAnges = SCNVector3(x:degToRad(20),y:degToRad(45), z:degToRad(78))
After you set this, go back and check to value of rotation:
SCNVector4(x: -0.16975601, y: 0.5943193, z: 0.786109, w: 1.448788)
This means there is an axis going through point(-0.16975601, 0.5943193, 0.786109) and origin (0,0,0), and node is rotating around it for 1.448788 (82 degree).

Rotate geometry around its local axis

I tried to rotate a geometry around its local axis, but haven't found a way to do so. I know that there is ST_Rotate (see https://postgis.net/docs/ST_Rotate.html) for 2D calculations and (among others) ST_RotateX (see https://postgis.net/docs/ST_RotateX.html), but these methods rotate a geometry around the origin. I also tried to abuse ST_Affine when I tried to change (what seems to be) the origin (namely 0/0/0):
SELECT ST_Affine(
ST_GeomFromText(ST_AsText(runway_area)),
1, 0, 0, 0,
cos(rotRadians), -sin(rotRadians), 0, sin(rotRadians), cos(rotRadians),
--- use the geometry's centroid instead of 0, 0, 0
ST_X(ST_GeomFromText(ST_AsText(runway_area))), ST_Y(ST_GeomFromText(ST_AsText(runway_area))), ST_Z(ST_GeomFromText(ST_AsText(runway_area)))
)
It didn't work out - all I got was something that was way away from the intended location. Do I miss a very fundamental method by PostGIS here to rotate a geometry around one of its local axis?
Take a closer look at ST_Rotate, and note that there are three argument signatures:
geometry ST_Rotate(geometry geomA, float rotRadians);
geometry ST_Rotate(geometry geomA, float rotRadians, float x0, float y0);
geometry ST_Rotate(geometry geomA, float rotRadians, geometry pointOrigin);
The x0 and y0 or pointOrigin arguments for the last two signatures do what your questions asks: they allow the rotation to pivot around a custom defined coordinate.
And an example from the docs show how to rotate a geometry 60 degrees clockwise around the centroid:
SELECT ST_AsEWKT(ST_Rotate(geom, radians(-60.0), ST_Centroid(geom)))
FROM (SELECT 'LINESTRING (50 160, 50 50, 100 50)'::geometry AS geom) AS foo;
st_asewkt
--------------------------------------------------------------
LINESTRING(116.4225 130.6721,21.1597 75.6721,46.1597 32.3708)
(1 row)
You could combine ST_Rotate with ST_Centroid, example:
ST_Rotate(ST_GeomFromText(ST_AsText(runway_area)), -pi()/3, ST_Centroid(ST_GeomFromText(ST_AsText(runway_area)))
The last three arguments to ST_Affine do not represent the origin, but the global "shift" in the affine transformation. The documentation for ST_RotateX shows how to use this function to generate a rotation around the x-axis. All these parameters are zero since it's a rotation without translation.
If you want to employ a general axis, you would need to construct the corresponding rotation matrix and substitute its elements for the arguments a,b,c,d,e,f,g,h,i of ST_Affine and set xoff, yoff, zoff to zero.

leaflet editable restrict draw to a specific area

In Leaflet.Editable I want to confine/limit my customers to draw only in a specific area/bounds.
actually im trying to limit them to (90, -90, 180, -180) bounds of map..
maxBounds: [[-90, -180], [90, 180]]
I was not able to find anything anywhere and it seems that i am missing something.
CODEPEN DEMO
please help.
EDIT:
the Y axis is blocking correctly and mouse cannot stretch shape beyond top and bottom.
the problem is in X axis (as seen in pictures)
as for now i solved it with after save check and clear shape if it out of map bounds (BAD USER EXPERIENCE). i need a mouse confinement just like y axis does.
Without knowing your use case (why the whole world map??) Quickest and easiest fix would be to simply set the map's minZoom to something a bit higher, for example, I found that minZoom: 5 was adequate except for cases where the map was both really short and really wide (which is rarely the case in most apps I've seen).
But the real fix involves writing your own custom overrides for dragging markers and shapes.
According to API doc the L.Editable plugin allows you to override a bunch of stuff including the VertexMarker class, via map.editTools.options.vertexMarkerClass.
Fixed codepen: http://codepen.io/anon/pen/GrPpRY?editors=0010
This snippet of code that allows you to constrain the longitude for dragging vertex markers by correcting values under -180 and over 180 is this:
// create custom vertex marker editor
var vertexMarkerClass = L.Editable.VertexMarker.extend({
onDrag: function(e) {
e.vertex = this;
var iconPos = L.DomUtil.getPosition(this._icon),
latlng = this._map.layerPointToLatLng(iconPos);
// fix out of range vertex
if (latlng.lng < -180) {
e.latlng.lng = latlng.lng = -180;
this.setLatLng(latlng);
}
if (latlng.lng > 180) {
e.latlng.lng = latlng.lng = 180;
this.setLatLng(latlng);
}
this.editor.onVertexMarkerDrag(e);
this.latlng.update(latlng);
this._latlng = this.latlng; // Push back to Leaflet our reference.
this.editor.refresh();
if (this.middleMarker) this.middleMarker.updateLatLng();
var next = this.getNext();
if (next && next.middleMarker) next.middleMarker.updateLatLng();
}
});
// attach custom editor
map.editTools.options.vertexMarkerClass = vertexMarkerClass;
I didn't code for dragging the shape as a whole (the rectangle, in this case). While the VertexMarker fix should address all kinds of vertex dragging, you need to override each shape's drag handler to properly constrain the bounds. And if bounds are exceeded, crop the shape appropriately. As was pointed out, Leaflet already does this for latitude, but because Leaflet allows wrapping the map around horizontally you have your essential problem. Using rec.on("drag") to correct the bounds when they cross over your min/max longitude is the only way to address it. It is basically the same solution as I have laid out for the vertexMarkerClass - actual code left as exercise for the diligent reader.

CGAffineTransform help, flipping a label

I basically have a pie chart where I have lines coming out of each segment of the pie chart. So in the case where the line comes out of the circle to the left, when I draw my text, it is reversed. "100%" would look like => "%001" (Note, the 1 and % sign are actually drawn in reverse to, like if a mirror. So the little overhang on top of the 1 points to the right, rather than the left.)
I tried reading through Apple's docs for the AffineTransform, but it doesn't make complete sense to me. I tried making this transformation matrix to start:
CGAffineTransform transform1 = CGAffineTransformMake(-1, 0, 0, 1, 0, 0);
This does flip the text around its x-axis so the text now looks correct on the left side of the circle. However, the text is now on the line, rather than at the end of the line like it originally was. So I thought I could translate it by moving the text in the x-axis direction by changing the tx value in the matrix. So instead of using the above matrix, I used this:
CGAffineTransform transform1 = CGAffineTransformMake(-1, 0, 0, 1, -strlen(t1AsChar), 0);
However, the text just stays where it's at. What am I doing wrong? Thanks.
strlen() doesn't give you the size of the rendered text box, it just gives you the length of the string itself (how many characters that string has). If you're using a UITextField you can use textField.frame.size.width instead.

Simulating an anchor point when translating in Core Gaphics

Core Graphics does not provide an anchor point property, and all transforming/translating assumes an anchor point 0,0 (lower left). Core Animation, does provide an anchor point, but we are not using CA.
Does anyone know how to modify a transformation matrix (used with CGAffineTransform) so that we can simulate different anchor point locations (e.g. bottom middle, center, etc)?
Thanks
Sure. Translate the desired point to 0, 0. Then apply whatever transformation you want. Then apply the inverse translation.
Say you want to rotate about the point 25, 25. Do this:
CGAffineTransform *t = CGAffineTransformMake();
t = CGAffineTransformTranslate(t, -25, -25);
t = CGAffineTransformRotate(t, angle);
t = CGAffineTransformTranslate(t, 25, 25);
At this point, t is a transform which will rotate by angle about 25, 25.