How to get real Coordinates on QML-Map - python-3.x

I have been trying to get the Map coordinates from my QML map with the MouseArea. But the problem is that I only get the coordinates of my mouse. It tells me the coordinates where the position of my mouse is on the window but not the coordinates of the click I made on the actual map. I don't know how to explain it better. Here is a part of my code:
MouseArea{
id: mouseArea
property var positionInRoot: mapFromItem(myItem, mouseX, mouseY)
anchors.fill: parent
acceptedButtons: Qt.LeftButton
hoverEnabled: true
onClicked: {
console.warn("Coordinates: " + mouseArea.positionInRoot)
}
}
My Question is now if it's possible to get the coordinates of the map where I made a mouseclick? And if yes how could I do it?

You should use the toCoordinate method of the QML map.
mapFromItem / mapToItem are used to map a point in a item to another item's coordinate system, but we're talking about QML Item's (x, y) and not QtPosition latitude, longitude. You still need those however, if your MouseArea and Map do not have the same x/y position
Here is an example that display a label with lat and lon under the mouse cursor.
Map {
id: map
anchors.fill: parent
plugin: mapPlugin
}
MouseArea
{
anchors.fill: map
hoverEnabled: true
property var coordinate: map.toCoordinate(Qt.point(mouseX, mouseY))
Label
{
x: parent.mouseX - width
y: parent.mouseY - height - 5
text: "lat: %1; lon:%2".arg(parent.coordinate.latitude).arg(parent.coordinate.longitude)
}
}
If you want to display coordinates in degrees minutes second format, you could use helper such as these:
function decimalToMinutes(value, isLat)
{
var letter = isLat?value>=0?"N":"S":value>=0?"E":"W"
var degrees = Math.floor(value)
var minutes = Math.floor((value-degrees)*60)
var seconds = ((value-degrees)*3600 - 60*minutes).toFixed(1)
return "%1° %2' %3\" %4".arg(degrees).arg(minutes).arg(seconds).arg(letter)
}
function minutesToDecimal(value)
{
var myRegexp = /(\d+)° (\d+)' (\d+(?:\.\d+)?)" ([NSEW])/g;
var match = myRegexp.exec(value)
if(match == null)
return 0
return ((match[4]=="N"||match[4]=="E")?1:-1)*(Number(match[1]) +Number( match[2]/60) + Number(match[3]/3600))
}
function coordinateToString(coordinate)
{
return decimalToMinutes(coordinate.latitude, true) + " " + decimalToMinutes(coordinate.longitude, false)
}

Related

Drawing rectangle underneath PIXI.Text

In a word game I am trying to draw score as white numbers above a blue (or red) rectangle:
For example, in the above screenshot it is the number "13".
Here is my entire class Score.js (with currently hardcoded WIDTH and HEIGHT):
"use strict";
function Score(color) {
PIXI.Container.call(this);
this.interactive = false;
this.buttonMode = false;
this.visible = false;
this.bgGraphics = new PIXI.Graphics();
this.bgGraphics.beginFill(color, 1);
this.bgGraphics.drawRect(0, 0, Score.WIDTH, Score.HEIGHT);
this.bgGraphics.endFill();
this.addChild(this.bgGraphics);
this.text = new PIXI.Text('XXX', {font: '20px Arial', fill: 0xFFFFFF});
this.text.x = 1;
this.text.y = 1;
this.addChild(this.text);
}
Score.prototype = Object.create(PIXI.Container.prototype);
Score.prototype.constructor = Score;
Score.WIDTH = 36;
Score.HEIGHT = 24;
Score.prototype.setText = function(str) {
this.text.text = str;
}
I wonder, how to modify my setText() function, so that a new rectangle is drawn on each call - as a bounding rectangle for the str argument?
I have looked at the PIXI.Text.getBounds() method, but it returns a Matrix and not a Rectangle...
I think you can just use this.text.width. This has historically had some bugs associated with it, but it should be working right in the latest version.

how to drag a svg rectangle in d3.js?

Here I am creating a rectangle named resize_icon within another rectangle positioning it at the lower right corner of the big rectangle.I want this resize_icon rectangle to be dragged freely within big rectangle but it should not cross the bigger rectangles top edge and its left edge but can be dragged anywhere else. resize[] is an array which contains id, x and y position of resize_icon rectangle.Please have a look at the following drag code and please tell me why is it not working. Also if anyone can suggest a proper code would be really helpful.
c.append('rect').attr('class','resize_icon').attr('fill','#000')
.attr('id','resize_icon'+i).attr('height',5).attr('width',5)
.attr('x', x+wd+35).attr('y',y+rectHeight-5).on(drag1)
.on('click',function(){
d3.selectAll(".selected").classed("selected",false);
d3.select(this.parentNode).classed("selected",true);
resize_rect(i);
});
var drag1 = d3.behavior.drag().origin(function()
{
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
})
.on("dragstart", dragstart)
.on("drag", dragon)
.on("dragend", dragstop);
function dragstart() {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("drag", true);
}
function dragon() {
d3.select(this).select('.resize_icon')
.attr("x", d3.event.x)
.attr("y", d3.event.y);
id = d3.select(this).select('.resize_icon').attr("id");
a = id.replace("resize_icon", "");
resize[a].x = d3.event.x;
resize[a].y = d3.event.y;
}
function dragstop() {
d3.select(this).classed("drag", false);
}
Here is my code to drag between a boundary :
function button_drag2(xLower,xHigher,yLower,yHigher){
return d3.behavior.drag()
.origin(function() {
var g = this;
return {x: d3.transform(g.getAttribute("transform")).translate[0],
y: d3.transform(g.getAttribute("transform")).translate[1]};
})
.on("drag", function(d) {
g = this;
translate = d3.transform(g.getAttribute("transform")).translate;
x = d3.event.dx + translate[0],
y = d3.event.dy + translate[1];
if(x<xLower){x=xLower;}
if(x>xHigher){x=xHigher;}
if(y<yLower){y=yLower;}
if(y>yHigher){y=yHigher;}
d3.select(g).attr("transform", "translate(" + x + "," + y + ")");
d3.event.sourceEvent.stopPropagation();
});
}
}
Then call on your selection :
var selection = "#rectangle"; //change this to whatever you want to select
d3.select(selection).call(button_drag2(-100,100,-100,100));
// this lets you drag the rectangle 100px left, right, up and down. Change to what ever you want :)
Hope this helps you out :)

UIScrollView horizontally scrolling menu in Sprite Kit

I am trying to make a horizontally scrolling menu in my new Sprite Kit game. With Sprite Kit being fairly new, there aren't many good tutorials for Sprite Kit. Is UIScrollView compatible with Sprite Kit? I have tried a couple of ways such as this:
UIScrollView *scroll = [[UIScrollView alloc] initWithFrame:CGRectMake(0, 0, self.view.frame.size.width, self.view.frame.size.height)];
scroll.pagingEnabled = YES;
NSInteger numberOfViews = 3;
for (int i = 0; i < numberOfViews; i++) {
CGFloat xOrigin = i * self.view.frame.size.width;
UIView *awesomeView = [[UIView alloc] initWithFrame:CGRectMake(xOrigin, 0, self.view.frame.size.width, self.view.frame.size.height)];
awesomeView.backgroundColor = [UIColor colorWithRed:0.5/i green:0.5 blue:0.5 alpha:1];
[scroll addSubview:awesomeView];
}
This didn't work. I have never worked with UIScrollView before. Can anyone give a way of making a horizontally scrolling menu using UIScrollView? An example of what I am trying to do is in Bloons TD 5 with their menu.
I know this question is a few months old already, but I was also looking for a scrolling menu slider to select levels in my SpriteKit game. I couldn't find any code already existing and the thought of force fitting the UIScrollView into my SpriteKit game didn't appeal to me. So I wrote my own sliding menu for level selection.
I've simulated all of the motions of the UIScrollView so it looks natural, and can be configured to scroll left - right or up - down.
As a bonus I created a parallax background on the menu which can be easily swapped out with new images for a custom look.
https://github.com/hsilived/ScrollingMenu
The code for the Scrolling menu is very well commented so there shouldn't be to many integration issues.
The github project has a fully working demo with Worlds, Levels and parallax images. If you like it mark my answer as useful so I can gain some reputation :)
You can definitely combine UIKit controls with SpriteKit. From the SKScene where you want to have a UIScrollView add the scroll view to self.view, like this: [self.view addSubview:scroll]. You may want to do this in the -(void)didMoveToView:(SKView*)view method on your SKScene. Remember to remove the scroll view from the SKView once you transition out of the SKScene that is using it. You can do that in the -(void)willMoveFromView:(SKView*)view method. Don't forget to set the contentSize on the UIScrollView.
Note: If you want to have SpriteKit nodes over the UIKit controls then you will need to write your own SKSpriteNode that behaves like a UIScrollView.
Expanding on Note: If your scroll view needs to contain Sprite-kit nodes and not just UIKit views you won't be able to use UIScrollView unless you do something clever as suggested in the answer to this question here
There are many ways to obtain it, through SKCameraNode, using UIKit elements like UIScrollView or other ways..
But I want to show you how to do it simple with SpriteKit only.
Conceptually, you should build a larger SKNode that containt every single voice of your menu where each element have an equal distance from the other.
You can simulate the scrolling with an SKAction that move our node to the center of the nearest visible element.
GameViewController:
class GameViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
guard let view = self.view as! SKView? else { return }
view.ignoresSiblingOrder = true
view.showsFPS = true
view.showsNodeCount = true
view.showsPhysics = false
view.showsDrawCount = true
let scene = GameScene(size:view.bounds.size)
scene.scaleMode = .aspectFill
view.presentScene(scene)
}
}
GameScene:
class GameScene: SKScene {
var lastX: CGFloat = 0.0
var moveableArea = SKNode() // the larger SKNode
var worlds: [String]! = ["world1","world2","world3"] // the menu voices
var currentWorld:Int = 0 // contain the current visible menu voice index
var worldsPos = [CGPoint]() // an array of CGPoints to store the menu voices positions
override func didMove(to view: SKView) {
//Make a generic title
let topLabel = SKLabelNode.init(text: "select world")
topLabel.fontSize = 40.0
self.addChild(topLabel)
topLabel.zPosition = 1
topLabel.position = CGPoint(x:frame.width / 2, y:frame.height*0.80)
// Prepare movable area
self.addChild(moveableArea)
moveableArea.position = CGPoint(x:self.frame.midX,y:self.frame.midY)
// Prepare worlds:
for i in 0..<worlds.count {
// add a title for i^ world
let worldLabel = SKLabelNode.init(text: worlds[i])
self.moveableArea.addChild(worldLabel)
worldLabel.position = CGPoint(x:CGFloat(i)*self.frame.width ,y:moveableArea.frame.height*0.60)
// add a sprite for i^ world
let randomRed = CGFloat(drand48())
let randomGreen = CGFloat(drand48())
let randomBlue = CGFloat(drand48())
let randomColor = UIColor(red: randomRed, green: randomGreen, blue: randomBlue, alpha: 1.0)
let worldSprite = SKSpriteNode.init(color: randomColor, size: CGSize.init(width: 100.0, height: 100.0))
worldSprite.name = worlds[i]
self.moveableArea.addChild(worldSprite)
worldSprite.position = CGPoint(x:CGFloat(i)*self.frame.width ,y:0.0)
}
}
func tapNode(node:SKNode,action:SKAction) {
if node.action(forKey: "tap") == nil {
node.run(action, withKey: "tap")
}
}
override func touchesBegan(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
let touchedNode = self.atPoint(location)
lastX = location.x
let sequence = SKAction.sequence([SKAction.scale(to: 1.5, duration: 0.2),SKAction.scale(to: 1.0, duration: 0.1)]) // a little action to show the selection of the world
switch touchedNode.name {
case "world1"?:
print("world1 was touched..")
tapNode(node:touchedNode,action:sequence)
case "world2"?:
print("world2 was touched..")
tapNode(node:touchedNode,action:sequence)
case "world3"?:
print("world3 was touched..")
tapNode(node:touchedNode,action:sequence)
default:break
}
}
override func touchesMoved(_ touches: Set<UITouch>, with event: UIEvent?) {
let touch = touches.first!
let location = touch.location(in: self)
let currentX = location.x
let leftLimit:CGFloat = CGFloat(worlds.count-1)
let rightLimit:CGFloat = 1.0
let scrollSpeed:CGFloat = 1.0
let newX = moveableArea.position.x + ((currentX - lastX)*scrollSpeed)
if newX < self.size.width*(-leftLimit) {
moveableArea.position = CGPoint(x:self.size.width*(-leftLimit), y:moveableArea.position.y)
}
else if newX > self.size.width*rightLimit {
moveableArea.position = CGPoint(x:self.size.width*rightLimit, y:moveableArea.position.y)
}
else {
moveableArea.position = CGPoint(x:newX, y:moveableArea.position.y)
}
// detect current visible world
worldsPos = [CGPoint]()
for i in 0..<worlds.count {
let leftLimit = self.size.width-(self.size.width*CGFloat(i))
let rightLimit = self.size.width-(self.size.width*CGFloat(i+1))
if rightLimit ... leftLimit ~= moveableArea.position.x {
currentWorld = i
}
worldsPos.append(CGPoint(x: (rightLimit + (leftLimit - rightLimit)/2), y:moveableArea.position.y))
}
lastX = currentX
}
override func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent?) {
if worldsPos.count>0, moveableArea.action(forKey: "moveAction") == nil {
let moveAction = SKAction.move(to: worldsPos[currentWorld], duration: 0.5)
moveAction.timingMode = .easeInEaseOut
self.moveableArea.run(moveAction, withKey: "moveAction")
}
}
}
As you can see , with touchesBegan we can detect the touched world and select it, with touchesMoved we are able to move the moveable node to the correct position and to detect the nearest visible world and with touchesEnded we can create the smooth scrolling to automatic move to the current nearest visible voice after a touch..
Output:

MKMapView image overlay drawing inverted

I am currently creating an app which requires numerous overlays of different sizes to be drawn on a map. The app currently grabs 2 map points which represent the corners of a bounding box in which the overlay should sit. I am then converting these in to a MKMapRect for use within an MKOverlay class. The overlay is drawing in the correct place but the image seems to be flipped vertically, I can tell this as I tried applying a CGAffineTransformMakeRotation(180) and the image appeared the right way around but the writing was backwards.
If anyone can help me figure out why it's doing this it would be much appreciated, please see my code below.
Setup code:
// First grab the overlay co-ordinates
double left = [[self.storedWildMapObject.arrayBBox objectAtIndex:0] doubleValue], bottom = [[self.storedWildMapObject.arrayBBox objectAtIndex:1] doubleValue], right = [[self.storedWildMapObject.arrayBBox objectAtIndex:2] doubleValue], top = [[self.storedWildMapObject.arrayBBox objectAtIndex:3] doubleValue];
// Store these in 2 coordinates representing top left / bot right
CLLocationCoordinate2D upperLeftCoord =
CLLocationCoordinate2DMake(top,left);
CLLocationCoordinate2D lowerRightCoord =
CLLocationCoordinate2DMake(bottom,right);
//Convert to map points
MKMapPoint upperLeft = MKMapPointForCoordinate(upperLeftCoord);
MKMapPoint lowerRight = MKMapPointForCoordinate(lowerRightCoord);
// Work out sizes
double rightToLeftDiff = lowerRight.y - upperLeft.y;
double topToBottDiff = lowerRight.x - upperLeft.x;
MKMapRect bounds = MKMapRectMake(upperLeft.x, upperLeft.y, topToBottDiff, rightToLeftDiff);
// Add to overlay
MKMapOverlay *mapOverlay = [[MKMapOverlay alloc] initWithMapRect:bounds andCoord:upperLeftCoord];
[self.mapV addOverlay:mapOverlay];
// Map overlay
- (id)initWithMapRect:(MKMapRect)rect andCoord:(CLLocationCoordinate2D)coord
{
self = [super init];
if (self)
{
self.centerPos = coord;
self.mkMapRect = rect;
}
return self;
}
-(CLLocationCoordinate2D)coordinate
{
return self.centerPos;
}
- (MKMapRect)boundingMapRect
{
return self.mkMapRect;
}
// MapOverlayView
- (id)initWithOverlay:(id <MKOverlay>)overlay andImage:(UIImage *)img
{
self = [super initWithOverlay:overlay];
if (self)
{
self.image = img;
}
return self;
}
/** Degrees to Radian **/
#define degreesToRadians( degrees ) ( ( degrees ) / 180.0 * M_PI )
/** Radians to Degrees **/
#define radiansToDegrees( radians ) ( ( radians ) * ( 180.0 / M_PI ) )
- (void)drawMapRect:(MKMapRect)mapRect zoomScale:(MKZoomScale)zoomScale inContext:(CGContextRef)ctx
{
CGImageRef imageReference = self.image.CGImage;
MKMapRect theMapRect = [self.overlay boundingMapRect];
CGRect theRect = [self rectForMapRect:theMapRect];
CGRect clipRect = [self rectForMapRect:mapRect];
CGContextAddRect(ctx, clipRect);
CGContextClip(ctx);
//CGContextRotateCTM(ctx, degreesToRadians(180.0f));
CGContextDrawImage(ctx, theRect, imageReference);
}
Just incase it helps I also have a debugger print out of theMapRect as used in MapOverlayView:
(lldb) p theMapRect
(MKMapRect) $17 = {
(MKMapPoint) origin = {
(double) x = 1.27662e+08
(double) y = 7.86099e+07
}
(MKMapSize) size = {
(double) width = 8.12378e+06
(double) height = 1.27474e+07
}
}
Below is what it looks like:
Have you verified that the storedwildmapobject has the coordinates in the order you are expecting them?
The answer found here seems to draw my image the correct way around with the same mapRect:
https://stackoverflow.com/a/3599448/1090024
So I'm using this for now.

selecting multiple svg elements and dragging them in Raphael.js

Does anyone know how I can achieve this?
I basically have an svg document with multiple shapes,lines,text,etc... and I am trying to implement a selection tool which helps me select multiple elements, group them and drag them.
There is a feature in raphäel called set: http://raphaeljs.com/reference.html#set
You can add all the elements you want to drag around to a new set and then apply the dragging mechanism to the set.
I made you this: http://jsfiddle.net/Wrajf/
It's not perfect. I would add the mousemove event to the document, but for that you need a library like jQuery. Otherwise if you move the mouse to fast you have a fall out.
I did this (example here) :
EDIT : something cleaner
Create methods to set and retrieve the group of an element :
Raphael.el.setGroup = function (group) {
this.group = group;
};
Raphael.el.getGroup = function () {
return this.group;
};
Create the method of your grouped elements :
Raphael.fn.superRect = function (x, y, w, h, text) {
var background = this.rect(x, y, w, h).attr({
fill: "#FFF",
stroke: "#000",
"stroke-width": 1
});
var label = this.text(x+w/2,y+h/2, text);
var layer = this.rect(x, y, w, h).attr({
fill: "#FFF",
"fill-opacity": 0,
"stroke-opacity": 0,
cursor: "move"
});
var group = this.set();
group.push(background, label, layer);
layer.setGroup(group);
return layer;
};
create functions to drag a grouped elements :
var dragger = function () {
this.group = this.getGroup();
this.previousDx = 0;
this.previousDy = 0;
},
move = function (dx, dy) {
var txGroup = dx-this.previousDx;
var tyGroup = dy-this.previousDy;
this.group.translate(txGroup,tyGroup);
this.previousDx = dx;
this.previousDy = dy;
},
up = function () {};
Init SVG paper and create your elements (the order of element is important)::
window.onload = function() {
var paper = Raphael(0, 0,"100%", "100%");
var x=50, y=50, w=30, h=20;
paper.superRect(x, y, w, h, "abc").drag(move, dragger, up);
x +=100;
paper.superRect(x, y, w, h, "def").drag(move, dragger, up);
};

Resources