I have a CustomPainter that looks like this:
class MyPainter extends CustomPainter {
Offset left, top, right, bottom;
MyPainter({this.left, this.top, this.right, this.bottom});
#override
void paint(Canvas canvas, Size size) {
Paint pp = Paint()
..color = Colors.blue
..strokeCap = StrokeCap.round
..strokeWidth = 10;
Paint p = Paint()
..color = Colors.red
..style = PaintingStyle.stroke
..strokeWidth = 2;
Path ph = Path();
ph.moveTo(left.dx, left.dy);
ph.quadraticBezierTo(top.dx, top.dy, right.dx, right.dy);
canvas.drawPoints(PointMode.points, [left, top, right, bottom], pp);
canvas.drawPath(ph, p);
}
#override
bool shouldRepaint(CustomPainter oldDelegate) {
return true;
}
}
And this is the result: https://imgur.com/a/m4i6WEA
However I want the curve line, pass the control point. Something like this: https://imgur.com/a/cumbAVz
How can I do that?!
You need to calculate internal control point P1 coordinates based on needed point top
At first, roughly evaluate parameter t for that point. Let it is 1/2 (if top lies near the middle of curve). Using quadratic Bezier formula:
P(t) = P0 * (1-t)^2 + 2 * P1 * t * (1-t) + P2 * t^2
top {for t=1/2} = P0 * 1/4 + P1 * 1/2 + P2 * 1/4
P1 = 2 * top - (P0 + P2) / 2
in components:
P1.dx = 2 * top.dx - (left.dx + right.dx) / 2
P1.dy = 2 * top.dy - (left.dy + right.dy) / 2
and finally
ph.quadraticBezierTo(P1.dx, P1.dy, right.dx, right.dy);
Related
I have two images with similar sizes that show similar scenes. How can we show two images in two frames and when panning or zooming in the left image, it pans and zooms in the right one? I don't want to concatenate the images though.
Is there a solution to do this? Both python or c++ OpenCV are fine.
About zoom in/out:
The basic idea is deciding the scale changed every time on mouse wheel. After you get the current scale (v.s. origin image) and correct region of image you want to show on screen, you can get the position and length of rectangle on scaled image. So you can draw this rectangle on scaled image.
In my github,checking OnMouseWheel () and RefreshSrcView () in Fastest_Image_Pattern_Matching/ELCVMatchTool/ELCVMatchToolDlg.cpp may give what you want.
About showing two images simutaneouly with same region:
use two picture boxes with MFC framework or other UI builder.
or use two cv::namedWindow () without framework
Effect:
Part of the code:
BOOL CELCVMatchToolDlg::OnMouseWheel (UINT nFlags, short zDelta, CPoint pt)
{
POINT pointCursor;
GetCursorPos (&pointCursor);
ScreenToClient (&pointCursor);
// TODO: 在此加入您的訊息處理常式程式碼和 (或) 呼叫預設值
if (zDelta > 0)
{
if (m_iScaleTimes == MAX_SCALE_TIMES)
return TRUE;
else
m_iScaleTimes++;
}
if (zDelta < 0)
{
if (m_iScaleTimes == MIN_SCALE_TIMES)
return TRUE;
else
m_iScaleTimes--;
}
CRect rect;
//GetWindowRect (rect);
GetDlgItem (IDC_STATIC_SRC_VIEW)->GetWindowRect (rect);//重要
if (m_iScaleTimes == 0)
g_dCompensationX = g_dCompensationY = 0;
int iMouseOffsetX = pt.x - (rect.left + 1);
int iMouseOffsetY = pt.y - (rect.top + 1);
double dPixelX = (m_hScrollBar.GetScrollPos () + iMouseOffsetX + g_dCompensationX) / m_dNewScale;
double dPixelY = (m_vScrollBar.GetScrollPos () + iMouseOffsetY + g_dCompensationY) / m_dNewScale;
m_dNewScale = m_dSrcScale * pow (SCALE_RATIO, m_iScaleTimes);
if (m_iScaleTimes != 0)
{
int iWidth = m_matSrc.cols;
int iHeight = m_matSrc.rows;
m_hScrollBar.SetScrollRange (0, int (m_dNewScale * iWidth - m_dSrcScale * iWidth) - 1 + BAR_SIZE);
m_vScrollBar.SetScrollRange (0, int (m_dNewScale * iHeight - m_dSrcScale * iHeight) - 1 + BAR_SIZE);
int iBarPosX = int (dPixelX * m_dNewScale - iMouseOffsetX + 0.5);
m_hScrollBar.SetScrollPos (iBarPosX);
m_hScrollBar.ShowWindow (SW_SHOW);
g_dCompensationX = -iBarPosX + (dPixelX * m_dNewScale - iMouseOffsetX);
int iBarPosY = int (dPixelY * m_dNewScale - iMouseOffsetY + 0.5);
m_vScrollBar.SetScrollPos (iBarPosY);
m_vScrollBar.ShowWindow (SW_SHOW);
g_dCompensationY = -iBarPosY + (dPixelY * m_dNewScale - iMouseOffsetY);
//滑塊大小
SCROLLINFO infoH;
infoH.cbSize = sizeof (SCROLLINFO);
infoH.fMask = SIF_PAGE;
infoH.nPage = BAR_SIZE;
m_hScrollBar.SetScrollInfo (&infoH);
SCROLLINFO infoV;
infoV.cbSize = sizeof (SCROLLINFO);
infoV.fMask = SIF_PAGE;
infoV.nPage = BAR_SIZE;
m_vScrollBar.SetScrollInfo (&infoV);
//滑塊大小
}
else
{
m_hScrollBar.SetScrollPos (0);
m_hScrollBar.ShowWindow (SW_HIDE);
m_vScrollBar.SetScrollPos (0);
m_vScrollBar.ShowWindow (SW_HIDE);
}
RefreshSrcView ();
return CDialogEx::OnMouseWheel (nFlags, zDelta, pt);
}
Since swipe() is deprecated, I am unable to swipe the screen from Left to Right. My App has 4 banners in it and I want to swipe to view all the banners.
This applies in all directions:
enum:
public enum DIRECTION {
DOWN, UP, LEFT, RIGHT;
}
actual code:
public static void swipe(MobileDriver driver, DIRECTION direction, long duration) {
Dimension size = driver.manage().window().getSize();
int startX = 0;
int endX = 0;
int startY = 0;
int endY = 0;
switch (direction) {
case RIGHT:
startY = (int) (size.height / 2);
startX = (int) (size.width * 0.90);
endX = (int) (size.width * 0.05);
new TouchAction(driver)
.press(startX, startY)
.waitAction(Duration.ofMillis(duration))
.moveTo(endX, startY)
.release()
.perform();
break;
case LEFT:
startY = (int) (size.height / 2);
startX = (int) (size.width * 0.05);
endX = (int) (size.width * 0.90);
new TouchAction(driver)
.press(startX, startY)
.waitAction(Duration.ofMillis(duration))
.moveTo(endX, startY)
.release()
.perform();
break;
case UP:
endY = (int) (size.height * 0.70);
startY = (int) (size.height * 0.30);
startX = (size.width / 2);
new TouchAction(driver)
.press(startX, startY)
.waitAction(Duration.ofMillis(duration))
.moveTo(startX, endY)
.release()
.perform();
break;
case DOWN:
startY = (int) (size.height * 0.70);
endY = (int) (size.height * 0.30);
startX = (size.width / 2);
new TouchAction(driver)
.press(startX, startY)
.waitAction(Duration.ofMillis(duration))
.moveTo(startX, endY)
.release()
.perform();
break;
}
}
usage:
swipe(driver,DIRECTION.RIGHT);
Hope this helps,
try below method. It works with Appium 1.16.0 version.
I created this method to swipe left or right based on a particular element location on the screen. It takes 3 parameters
Element X: It is the X coordinate of the element on which swipe touch action needs to be performed.
Element Y: It is the Y coordinate of the element.
Direction: Left/Right
//method to left and right swipe on the screen based on coordinates
public void swipeAction(int Xcoordinate, int Ycoordinate, String direction) {
//get device width and height
Dimension dimension = driver.manage().window().getSize();
int deviceHeight = dimension.getHeight();
int deviceWidth = dimension.getWidth();
System.out.println("Height x Width of device is: " + deviceHeight + " x " + deviceWidth);
switch (direction) {
case "Left":
System.out.println("Swipe Right to Left");
//define starting and ending X and Y coordinates
int startX=deviceWidth - Xcoordinate;
int startY=Ycoordinate; // (int) (height * 0.2);
int endX=Xcoordinate;
int endY=Ycoordinate;
//perform swipe from right to left
new TouchAction((AppiumDriver) driver).longPress(PointOption.point(startX, startY)).moveTo(PointOption.point(endX, endY)).release().perform();
break;
case "Right":
System.out.println("Swipe Left to Right");
//define starting X and Y coordinates
startX=Xcoordinate;
startY=Ycoordinate;
endX=deviceWidth - Xcoordinate;
endY=Ycoordinate;
//perform swipe from left to right
new TouchAction((AppiumDriver) driver).longPress(PointOption.point(startX, startY)).moveTo(PointOption.point(endX, endY)).release().perform();
break;
}
}
To fetch the element X,Y coordinates. try below methods
int elementX= driver.findElement(elementLocator).getLocation().getX();
int elementY= driver.findElement(elementLocator).getLocation().getY();
Assuming you created driver instance of AndroidDriver you can swipe left:
// Get location of element you want to swipe
WebElement banner = driver.findElement(<your_locator>);
Point bannerPoint = banner.getLocation();
// Get size of device screen
Dimension screenSize = driver.manage().window().getSize();
// Get start and end coordinates for horizontal swipe
int startX = Math.toIntExact(Math.round(screenSize.getWidth() * 0.8));
int endX = 0;
TouchAction action = new TouchAction(driver);
action
.press(PointOption.point(startX, bannerPoint.getY()))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(500)))
.moveTo(PointOption.point(endX, bannerPoint.getY()))
.release();
driver.performTouchAction(action);
Use latest appium-java-client 6.1.0 and Appium 1.8.x server
This should work,
Dimension size = driver.manage().window().getSize();
System.out.println(size.height+"height");
System.out.println(size.width+"width");
System.out.println(size);
int startPoint = (int) (size.width * 0.99);
int endPoint = (int) (size.width * 0.15);
int ScreenPlace =(int) (size.height*0.40);
int y=(int)size.height*20;
TouchAction ts = new TouchAction(driver);
//for(int i=0;i<=3;i++) {
ts.press(PointOption.point(startPoint,ScreenPlace ))
.waitAction(WaitOptions.waitOptions(Duration.ofMillis(1000)))
.moveTo(PointOption.point(endPoint,ScreenPlace )).release().perform();
This is for iOS Mobile:
//Here i am trying to swipe list of images from right to left
//First i am getting parent element (table/cell) id
//Then using predicatestring am searching for the element present or not then trying to click
List<MobileElement> ele = getMobileElement(listBtnQuickLink).findElements(By.xpath(".//XCUIElementTypeButton"));
for(int i=1 ;i<=20;i++) {
MobileElement ele1 = ele.get(i);
String parentID = getMobileElement(listBtnQuickLink).getId();
HashMap<String, String> scrollObject = new HashMap<String, String>();
scrollObject.put("element", parentID); //This is parent element id (not same element)
scrollObject.put("predicateString", "label == '"+ele1.getText()+"'");
scrollObject.put("direction", "left");
driver.executeScript("mobile:swipe", scrollObject); // scroll to the target element
System.out.println("Element is visible : "+ele1.isDisplayed());
}
Unfortunately i was noted that TouchAction doesn't work on Android 11 with Selenium 4. So if you use Selenide and Appium you can try this:
public class SwipeToLeft implements Command<SelenideElement> {
#Nullable
#Override
public SelenideElement execute(SelenideElement proxy, WebElementSource locator, #Nullable Object[] args) throws IOException {
Selenide.sleep(2000);
var driver = WebDriverRunner.getWebDriver();
var element = proxy.getWrappedElement();
((JavascriptExecutor) driver).executeScript("mobile: swipeGesture", ImmutableMap.of(
"elementId", ((RemoteWebElement) element).getId(),
"direction", "left",
"percent", 0.75
));
return proxy;
}
}
And then you can use:
$('your seleniumLocator').shouldBe(visible).execute(new SwipeToLeft());
I'm trying to draw a triangle like this one in a view (one UIView, one NSView):
My first thought was CoreGraphics, but I couldn't find any information that would help me draw a gradient between three points of arbitrary color.
Any help?
Thanks!
Actually it's pretty simple with CoreGraphics. Below you can find code that renders given triangle, but first let's think how we can solve this problem.
Theory
Imagine equilateral triangle with side length w. All three angles are equal to 60 degrees:
Each angle will represent component of a pixel: red, green or blue.
Lets analyze intensity of a green component in a pixel near top angle:
The more closer pixel to the angle, the more component intense it'll have and vice versa. Here we can decompose our main goal to smaller ones:
Draw triangle pixel by pixel.
For each pixel calculate value for each component based on distance from corresponding angle.
To solve first task we will use CoreGraphics bitmap context. It will have four components per pixel each 8 bits long. This means that component value may vary from 0 to 255. Fourth component is alpha channel and will be always equal to max value - 255. Here is example of how values will be interpolated for the top angle:
Now we need to think how we can calculate value for component.
First, let's define main color for each angle:
Now let's choose an arbitrary point A with coordinates (x,y) on the triangle:
Next, we draw a line from an angle associated with red component and it passes through the A till it intersects with opposite side of a triangle:
If we could find d and c their quotient will equal to normalized value of component, so value can be calculated easily:
(source: sciweavers.org)
Formula for finding distance between two points is simple:
(source: sciweavers.org)
We can easily find distance for d, but not for c, because we don't have coordinates of intersection. Actually it's not that hard. We just need to build line equations for line that passes through A and line that describes opposite side of a triangle and find their intersection:
Having intersection point we can apply distance formula to find c and finally calculate component value for current point.
Same flow applies for another components.
Code
Here is the code that implements concepts above:
+ (UIImage *)triangleWithSideLength:(CGFloat)sideLength {
return [self triangleWithSideLength:sideLength scale:[UIScreen mainScreen].scale];
}
+ (UIImage *)triangleWithSideLength:(CGFloat)sideLength
scale:(CGFloat)scale {
UIImage *image = nil;
CGSize size = CGSizeApplyAffineTransform((CGSize){sideLength, sideLength * sin(M_PI / 3)}, CGAffineTransformMakeScale(scale, scale));
size_t const numberOfComponents = 4;
size_t width = ceilf(size.width);
size_t height = ceilf(size.height);
size_t realBytesPerRow = width * numberOfComponents;
size_t alignedBytesPerRow = (realBytesPerRow + 0xFF) & ~0xFF;
size_t alignedPixelsPerRow = alignedBytesPerRow / numberOfComponents;
CGContextRef ctx = CGBitmapContextCreate(NULL,
width,
height,
8,
alignedBytesPerRow,
CGColorSpaceCreateDeviceRGB(),
(CGBitmapInfo)kCGImageAlphaPremultipliedLast);
char *data = CGBitmapContextGetData(ctx);
for (int i = 0; i < height; i++) {
for (int j = 0; j < width; j++) {
int edge = ceilf((height - i) / sqrt(3));
if (j < edge || j > width - edge) {
continue;
}
CGFloat redNormalized = 0;
CGFloat greenNormalized = 0;
CGFloat blueNormalized = 0;
CGPoint currentTrianglePoint = (CGPoint){j / scale, (height - i) / scale};
[self calculateCurrentValuesAtGiventPoint:currentTrianglePoint
sideLength:sideLength
sideOne:&redNormalized
sideTwo:&greenNormalized
sideThree:&blueNormalized];
int32_t red = redNormalized * 0xFF;
int32_t green = greenNormalized * 0xFF;
int32_t blue = blueNormalized * 0xFF;
char *pixel = data + (j + i * alignedPixelsPerRow) * numberOfComponents;
*pixel = red;
*(pixel + 1) = green;
*(pixel + 2) = blue;
*(pixel + 3) = 0xFF;
}
}
CGImageRef cgImage = CGBitmapContextCreateImage(ctx);
image = [[UIImage alloc] initWithCGImage:cgImage];
CGContextRelease(ctx);
CGImageRelease(cgImage);
return image;
}
+ (void)calculateCurrentValuesAtGiventPoint:(CGPoint)point
sideLength:(CGFloat)length
sideOne:(out CGFloat *)sideOne
sideTwo:(out CGFloat *)sideTwo
sideThree:(out CGFloat *)sideThree {
CGFloat height = sin(M_PI / 3) * length;
if (sideOne != NULL) {
// Side one is at 0, 0
CGFloat currentDistance = sqrt(point.x * point.x + point.y * point.y);
if (currentDistance != 0) {
CGFloat a = point.y / point.x;
CGFloat b = 0;
CGFloat c = -height / (length / 2);
CGFloat d = 2 * height;
CGPoint intersection = (CGPoint){(d - b) / (a - c), (a * d - c * b) / (a - c)};
CGFloat currentH = sqrt(intersection.x * intersection.x + intersection.y * intersection.y);
*sideOne = 1 - currentDistance / currentH;
} else {
*sideOne = 1;
}
}
if (sideTwo != NULL) {
// Side two is at w, 0
CGFloat currentDistance = sqrt(pow((point.x - length), 2) + point.y * point.y);
if (currentDistance != 0) {
CGFloat a = point.y / (point.x - length);
CGFloat b = height / (length / 2);
CGFloat c = a * -point.x + point.y;
CGFloat d = b * -length / 2 + height;
CGPoint intersection = (CGPoint){(d - c) / (a - b), (a * d - b * c) / (a - b)};
CGFloat currentH = sqrt(pow(length - intersection.x, 2) + intersection.y * intersection.y);
*sideTwo = 1 - currentDistance / currentH;
} else {
*sideTwo = 1;
}
}
if (sideThree != NULL) {
// Side three is at w / 2, w * sin60 degrees
CGFloat currentDistance = sqrt(pow((point.x - length / 2), 2) + pow(point.y - height, 2));
if (currentDistance != 0) {
float dy = point.y - height;
float dx = (point.x - length / 2);
if (fabs(dx) > FLT_EPSILON) {
CGFloat a = dy / dx;
CGFloat b = 0;
CGFloat c = a * -point.x + point.y;
CGFloat d = 0;
CGPoint intersection = (CGPoint){(d - c) / (a - b), (a * d - b * c) / (a - b)};
CGFloat currentH = sqrt(pow(length / 2 - intersection.x, 2) + pow(height - intersection.y, 2));
*sideThree = 1 - currentDistance / currentH;
} else {
*sideThree = 1 - currentDistance / height;
}
} else {
*sideThree = 1;
}
}
}
Here is a triangle image produced by this code:
For example, I have a latitude and longitude in decimal format (as opposed to Degrees-Hours-Minutes like lat=44.1° 9.5' 30''). To search for nearby objects, I must specify the search "radius" as a rectangle with four values:
north = 44.1;
south = -9.9;
east = -22.4;
west = 55.2;
Is there a formula or rule of thumb how to convert decimal lat/long values into a rectangular bounding box, such that the given latitude/longitude is in the center of that box?
Must I fiddle around myself with a WGS84 ellipsoid algorithm or are there open solutions to the problem?
I had exactly this problem and the solution isn't that straight forward, but the good news is that after a lot of work (and a great deal of help from SO and Google) I think I've cracked it.
There are lots of libraries around such as Proj4 which offer a multitude of algorithms to perform the required transformations, but coming at it cold I found it all a bit confusing and ended up writing my own code (I always like to know how things work).
My solution is based on ECEF and it works like this...
As I'm sure you've figured out, lines of latitude are always the same distance apart (the distance between 10 degrees and 20 degrees is the same as that between 20 and 30), but lines of longitude converge to meet at the poles. So the distance between 10 degrees and 20 degrees longitude at the equator is much larger than near the poles (and is 0 at the poles).
So you can easily work out how many metres between 2 degrees of latitude, but to do this with longitude you have to take the latitude into account.
Near the equator 1 degree of lat is pretty much the same distance as 1 degree of long, so if the map we're projecting had it's centre (0, 0) we can simply multiply lat and long by a constant to get metres from the map centre for any given point.
So my algorithm effectively rotates the globe until the actual centre of the map is at 0, 0.
So say the centre is really at (50.52, -4.82) - which it is in my case.
Imagine you're holding a globe and looking down on it with 0 lat, 0 long directly below you in the visible centre.
What we need to do is take our globe which currently has (0, 0) directly below us and rotate it in a westward (to the right) direction until (0, -4.82) is below us.
Then we rotate the globe southward (down) until (50.52, -4.82) is below us.
As a third step, we may then want to rotate it clockwise or anti-clockwise to correct for the orientation of the map with respect to true north (if true north is straight up on your map or if all you are interested in is distance not bearing, you won't need to do this)
So conceptually that's what we need to do, but how does that relate to our algorithm?
The answer is a transform (class) where we feed in three angles of rotation. This class has a public function which, given a lat/long pair, will return a new lat/long pair of that point on the globe after rotation.
And once we've done that, knowing the radius of the earth, we can convert this new pair into x and y coordinates, representing the distance from our map origin.
I should mention here that the earth is wider at the equator than it is at the poles, but the maths to deal with this is quite simply not worth the bother. However you calculate your x, y coords they will always be slightly out since the earth is not flat and for me, the code presented below does the job.
If your map is very close to the poles I suspect the results from this algorith may become quite inaccurate - basically lat/long doesn't really work very well at the poles (just take a look at google earth from above).
The MapTransform class requires you to setup a few things.
setRadius(1000); sets up the transform to work with a sphere of radius 1000 (units)
setBody("EARTH"); sets up the transform with the mean radius of the earth (in metres)
setRotation(x, y, z); sets up the transform to rotate about Z axis by z degrees, Y axis by y degrees then X axis by x degrees.
- basically, given your centre point (lat, long) and given that true north on the map is straight up, you would need the following: setRotation(0, lat, -long);
- the order of rotation is very important here and based on a coordinate system (looking back at the globe you're holding) where the Z axis coincides with the rotation of the earth, the Y axis rotates the closest surface of the globe up/down and the X axis is the axis you are looking along - hope this makes sense, it's a difficult concept to describe - see Rotation Matrix
Given your requirement to map from lat/long to metres from a specific point, the above should be all you need.
The function getMapPosition(lat, long) will return a double[] containing x, y in map units (metres if radius was specified in metres) from your origin
My class goes a bit further in terms of applying the coordinates to a specific map tile...
setMapOrigin(x, y); sets up where the rotational origin of the map (the point directly below the observer after rotation) is in relation to the bottom left corner of your map. Nominally this should be in metres (certainly if you used setBody("EARTH");) but needs to be in the same units as the specified radius.
setMapSize(w, h); sets up the size of the map in metres or what ever units you decided to use.
Finally, setBitmapSize(w, h) allows you to describe the size of the bitmap (in pixels) onto which you are projecting your map. In my application I have a bitmap representation of the map area and use the transform to supply the exact coorinates of the pixel on my bitmap where a point should be plotted. However, this isn't part of the question you asked so you may not need it.
Really hope this helps - seems just as long winded and complicated as all the examples I was looking at a month ago now.
import java.text.DecimalFormat;
public class MapTransform {
private double circumference;
private RotationMatrix rotationMatrix;
private double originX;
private double originY;
private double mapWidth;
private double mapHeight;
private int bitmapWidth;
private int bitmapHeight;
public MapTransform() {
this.circumference = 0;
this.rotationMatrix = new RotationMatrix();
this.rotationMatrix.makeIdentity();
this.originX = 0;
this.originY = 0;
this.mapWidth = 0;
this.mapHeight = 0;
this.bitmapWidth = 0;
this.bitmapHeight = 0;
}
public void setCircumference(double circumference) {
this.circumference = circumference;
}
public void setRadius(double radius) {
this.circumference = 2 * Math.PI * radius;
}
public void setBody(String body) {
if (body.toUpperCase().equals("EARTH")) {
setRadius(6371009); //mean radius of the earth in metres
// setRadius(6378137); //equatorial radius of the earth in metres
// setRadius(6356752); //polar radius of the earth in metres
}
else {
setRadius(0);
}
}
public void setRotation(double xRotateDegrees, double yRotateDegrees, double zRotateDegrees) {
RotationMatrix xMatrix = new RotationMatrix();
RotationMatrix yMatrix = new RotationMatrix();
RotationMatrix zMatrix = new RotationMatrix();
xMatrix.makeRotateX(Math.toRadians(xRotateDegrees));
yMatrix.makeRotateY(Math.toRadians(yRotateDegrees));
zMatrix.makeRotateZ(Math.toRadians(zRotateDegrees));
this.rotationMatrix = zMatrix.concatenate(yMatrix).concatenate(xMatrix);
}
public void setMapOrigin(double originX, double originY) {
this.originX = originX;
this.originY = originY;
}
public void setMapSize(double width, double height) {
this.mapWidth = width;
this.mapHeight = height;
}
public void setBitmapSize(int width, int height) {
this.bitmapWidth = width;
this.bitmapHeight = height;
}
public double[] getMapPosition(double[] geoPosition) {
return getMapPosition(geoPosition[0], geoPosition[1]);
}
public double[] getMapPosition(double latitude, double longitude) {
// convert the GeoPosition into an NVector
NVector vec = new NVector(latitude, longitude);
// rotate the vector in 3D
vec = rotationMatrix.transform(vec);
// convert the vector into 2D units by applying circumference to latitude/longitude and adding origins
double x = vec.getLongitude() * this.circumference / 360;
double y = vec.getLatitude() * this.circumference / 360;
// return a MapPosition
return new double[] {x, y};
}
public float[] getPixelPosition(double[] mapPosition) {
return getPixelPosition(mapPosition[0], mapPosition[1]);
}
public float[] getPixelPosition(double mapX, double mapY) {
// apply origin and scale based on map and bitmap widths
float x = (float) ((this.originX + mapX) * this.bitmapWidth / this.mapWidth);
// apply origin and scale based on map and bitmap heights, but invert to measure from top left instead of bottom left
float y = (float) (this.bitmapHeight - (this.originY + mapY) * this.bitmapHeight / this.mapHeight);
return new float[] {x, y};
}
public class RotationMatrix {
String name = "";
public double array [][] = {{0,0,0},{0,0,0},{0,0,0}};
public RotationMatrix() {}
public RotationMatrix(String name) {
this.name = name;
}
public void makeIdentity() {
for(int x = 0; x <= 2; x++) {
for (int y = 0; y <= 2; y++) {
array[x][y] = (x == y)? 1: 0;
}
}
}
public void makeRotateX(double thetaRadians) {
double cosTheta = Math.cos(thetaRadians);
double sinTheta = Math.sin(thetaRadians);
makeIdentity();
array[1][1] = cosTheta;
array[2][1] = -sinTheta;
array[1][2] = sinTheta;
array[2][2] = cosTheta;
}
public void makeRotateY(double thetaRadians) {
double cosTheta = Math.cos(thetaRadians);
double sinTheta = Math.sin(thetaRadians);
makeIdentity();
array[0][0] = cosTheta;
array[2][0] = sinTheta;
array[0][2] = -sinTheta;
array[2][2] = cosTheta;
}
public void makeRotateZ(double thetaRadians) {
double cosTheta = Math.cos(thetaRadians);
double sinTheta = Math.sin(thetaRadians);
makeIdentity();
array[0][0] = cosTheta;
array[1][0] = -sinTheta;
array[0][1] = sinTheta;
array[1][1] = cosTheta;
}
public NVector transform(NVector vec) {
NVector vec2 = new NVector();
vec2.x = vec.x * array[0][0] + vec.y * array[1][0] + vec.z * array[2][0];
vec2.y = vec.x * array[0][1] + vec.y * array[1][1] + vec.z * array[2][1];
vec2.z = vec.x * array[0][2] + vec.y * array[1][2] + vec.z * array[2][2];
return vec2;
}
public void output() {
if (this.name != null && this.name.length() == 0) {
System.out.println(this.name + "-------");
}
DecimalFormat df = new DecimalFormat("0.00");
for(int y = 0; y <= 2; y++) {
String out = "| ";
double test = 0;
for(int x = 0; x <= 2; x++) {
String f = df.format(array[x][y]);
if (f.length() < 5) f = " " + f;
out += f + " ";
test = test + array[x][y] * array[x][y];
}
if (test > 0.99 && test < 1.01) {test = 1.0;}
out += "| (=" + test + ")";
System.out.println(out);
}
System.out.println();
}
public RotationMatrix concatenate(RotationMatrix m2) {
RotationMatrix outputMatrix = new RotationMatrix();
for(int x = 0; x <= 2; x++) {
for(int y = 0; y <=2; y++) {
outputMatrix.array[x][y] = 0;
for (int q = 0; q <= 2; q++) {
outputMatrix.array[x][y] += this.array[x][q] * m2.array[q][y];
}
}
}
return outputMatrix;
}
}
public class NVector {
double x;
double y;
double z;
public NVector() {
this.x = 0;
this.y = 0;
this.z = 0;
}
public NVector(double x, double y, double z) {
this.x = x;
this.y = y;
this.z = z;
}
public NVector(double latitude, double longitude) {
setLatitudeLongitude(latitude, longitude);
}
public NVector(double[] geoPosition) {
setLatitudeLongitude(geoPosition[0], geoPosition[1]);
}
private void setLatitudeLongitude(double latitude, double longitude) {
double latitudeRadians = Math.toRadians(latitude);
double longitudeRadians = Math.toRadians(longitude);
double cosLatitude = Math.cos(latitudeRadians);
double cosLongitude = Math.cos(longitudeRadians);
double sinLatitude = Math.sin(latitudeRadians);
double sinLongitude = Math.sin(longitudeRadians);
this.x = cosLatitude * cosLongitude;
this.y = cosLatitude * sinLongitude;
this.z = sinLatitude;
}
public double getLatitude() {
return Math.toDegrees(Math.atan2(this.z, Math.sqrt(this.x * this.x + this.y * this.y)));
}
public double getLongitude() {
return Math.toDegrees(Math.atan2(this.y, this.x));
}
public double[] getGeoPosition() {
double[] geoPosition = new double[] {this.getLatitude(), this.getLongitude()};
return geoPosition;
}
public void output() {
output("");
}
public void output(String name) {
if (name != null && name.length() == 0) {
System.out.println("NVector: " + name);
}
DecimalFormat df = new DecimalFormat("0.00");
String vector = df.format(this.x) + "," + df.format(this.y) + "," + df.format(this.z);
String coords = "";
try {
coords = df.format(Math.toDegrees(this.getLatitude())) + "N " + df.format(Math.toDegrees(this.getLongitude())) + "E";
}
catch(Exception e) {
coords = "(coords unknown)";
}
System.out.println("(" + vector + ") at " + coords);
}
}
}
If you're talking about coordinates on a globe, isn't there really no clear definition of "rectangular bounding box" on a manifold?
Couldn't you just approximate the center of the "box" by averaging the dimensions of the rectagle as in cartesian coordinates:
x_center = x_left + (x_right - x_left) / 2
y_center = y_bottom + (y_top - y_bottom) / 2
Why not do a range/bearing from your center point to define the lat/lon's of the corner of the box (if that is what you are asking)? Use the four bearings 45 deg, 135 deg, 225 deg, 315 deg. See this web site for "Destination point from range/bearing": http://www.movable-type.co.uk/scripts/latlong.html
Another, much shorter answer to my other post if you just need the distance to another lat long from your point of origin which I found on SO (surprise).
Original answer was here, but I believe the code you're looking for is...
public static double distFrom(double lat1, double lng1, double lat2, double lng2) {
double earthRadius = 6371009; //mean radius of the earth in metres
double dLat = Math.toRadians(lat2-lat1);
double dLng = Math.toRadians(lng2-lng1);
double a = Math.sin(dLat/2) * Math.sin(dLat/2) +
Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) *
Math.sin(dLng/2) * Math.sin(dLng/2);
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));
double dist = earthRadius * c;
return dist;
}
The function below returns points on a sphere with a given radius. I want to add restriction such that points cannot be plotted within 30 degrees of the poles of the sphere.
public static function randomPoint(radius:Number):Number3D
{
var inclination:Number = Math.random() * Math.PI*2;
var azimuth:Number = Math.random() * Math.PI*2;
var point:Number3D = new Number3D(
radius * Math.sin(inclination) * Math.cos(azimuth),
radius * Math.sin(inclination) * Math.sin(azimuth),
radius * Math.cos(inclination)
);
return point;
}
Thanks in advance!
Sounds like you can just restrict the inclination:
var inclination:Number = (Math.PI/6) + Math.random()*(2*Math.PI-2*Math.PI/6)
Feel free to resolve those constant values, just kept them in to show the working.
Here's what I have so far... this does what I want, restricted north and south poles. Any improvements welcome!
var point:Number3D = sphericalPoint(100, inclination, azimuth);
public static function sphericalPoint(r:Number, inc:Number, az:Number):Number3D
{
var point:Number3D = new Number3D(
r * Math.sin(inc) * Math.cos(az),
r * Math.sin(inc) * Math.sin(az),
r * Math.cos(inc)
);
//cheat and use a transform matrix
var obj:Object3D = new Object3D();
obj.rotationX = 90;
point.rotate(point, obj.transform);
return point;
}
//a number between 0 and 180
protected function get inclination():Number
{
//default
//_inclination = Math.random() * Math.PI;
//number between 60 and 120
_inclination = Math.random() * (Math.PI*5/6 - Math.PI/6) + Math.PI/6;
return _inclination;
}
//a number between 0 and 360
protected function get azimuth():Number
{
//revolve around the Y axis
_azimuth = Math.random() * Math.PI*2;
return _azimuth;
}