Scenario:
I want a user to see a map and their current position. Then, if they click "start", navigation will begin and they'll see their "route" drawn onto the map as their position changes, similar to how some fitness apps work that map out your run/walk. The goal is to do this in real-time as the user's position changes.
Options:
The way I see it, there are two options: 1) use a RouteQuery and Map.AddRoute from the starting position, to the next position (when the position changes), keeping track of the last position, and always drawing a new MapRoute from that position to the new, or 2) displaying the user's current position as a dot that moves as their position changes, and then maybe when they press "stop", draw a MapRoute for each of their positions in order to show their full route.
I'd really prefer option #1 because the user can see their route progression, etc., as they go.
Here is the code that I'm using:
XAML:
<maps:Map x:Name="MainMap" />
<Button x:Name="btnStart" Content="Start"/>
<Button x:Name="btnStop" Content="Stop" IsEnabled="False"/>
Code-behind:
Global Variables:
GeoCoordinateWatcher watcher;
List<GeoCoordinate> listCoordinates;
GeoCoordinate lastCoordinate;
btnStart.Tap():
private void btnStart_Tap(object sender, GestureEventArgs e)
{
if (watcher == null)
{
watcher = new GeoCoordinateWatcher(GeoPositionAccuracy.High);
watcher.MovementThreshold = 20;
watcher.StatusChanged += watcher_StatusChanged;
watcher.PositionChanged += watcher_PositionChanged;
}
watcher.Start();
}
watcher.StatusChanged():
private void watcher_StatusChanged(object sender, GeoPositionStatusChangedEventArgs e)
{
switch (e.Status)
{
case GeoPositionStatus.Initializing:
btnStart.IsEnabled = false;
btnStop.IsEnabled = true;
break;
case GeoPositionStatus.NoData:
lblStatus.Text = "location data is not available.";
break;
case GeoPositionStatus.Ready:
lblStatus.Text = "location data is available.";
break;
}
}
watcher.PositionChanged():
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e)
{
if (listCoordinates == null)
{
// first time through:
listCoordinates = new List<GeoCoordinate>();
listCoordinates.Add(e.Position.Location);
lastCoordinate = e.Position.Location;
return;
}
else
{
listCoordinates.Add(e.Position.Location);
DrawRoute(e.Position.Location);
lastCoordinate = e.Position.Location;
}
}
DrawRoute function:
private void DrawRoute(GeoCoordinate newPosition)//
{
RouteQuery query = new RouteQuery()
{
TravelMode = TravelMode.Driving,
Waypoints = new List<GeoCoordinate>() { MainMap.Center, newPosition }
};
query.QueryCompleted += RouteQueryCompleted;
query.QueryAsync();
MainMap.Center = newPosition;
lastCoordinate = newPosition;
}
And finally, RouteQueryCompleted():
void RouteQueryCompleted(object sender, QueryCompletedEventArgs<Route> e)
{
mapRoute = new MapRoute(e.Result);
MainMap.AddRoute(mapRoute);
}
What happens:
It appears to work for a second as I begin driving, a short line is drawn where my start position is, but then about 10 second in, a line is randomly drawn down a nearby street (probably equivalent to 3 or 4 blocks long) and then down another block on a side road (while the whole time I haven't even driven ONE block, let alone make any turns!). It's very bizarre and definitely not accurate. I can upload a screenshot to better illustrate it if need be.
Can anyone see what I'm doing wrong in my code or is there a better way to accomplish this? I wasn't sure if this was the best way but I wasn't able to find any examples suggesting otherwise.
I ended up using MapPolyLine to draw a line between the last GeoCoordinate and the new one.
MapPolyline line = new MapPolyline();
line.StrokeColor = Colors.Blue;
line.StrokeThickness = 15;
line.Path.Add(lastCoordinate);
line.Path.Add(pos);
MainMap.MapElements.Add(line);
I am not sure why you are using RouteQuery for your task. Generally, you use this when you want the map sdk to determine a route for you given a set of coordinates. In your case however, you always know where you are through PositionChanged event. It will be easier to plot directly on the map as you move.
Something like this
void watcher_PositionChanged(object sender, GeoPositionChangedEventArgs<GeoCoordinate> e) {
Plot(e.Position.Location);
}
void Plot(GeoCoordinate pos) {
var ellipse = new Ellipse();
ellipse.Fill = new SolidColorBrush(System.Windows.Media.Colors.Blue);
ellipse.Height = 15;
ellipse.Width = 15;
ellipse.Opacity = 25;
var mapOverlay = new MapOverlay();
mapOverlay.Content = ellipse;
mapOverlay.PositionOrigin = new System.Windows.Point(0.5, 0.5);
mapOverlay.GeoCoordinate = pos;
var mapLayer = new MapLayer();
mapLayer.Add(mapOverlay);
MainMap.Layers.Add(mapLayer);
}
Related
I am binding the ImageSource of an ImageBrush on Windows Phone 8.1 (RT not silverlight) to a remote uri, and have a handler when the image fails to show a default image:
<ImageBrush Stretch="UniformToFill" ImageSource="{Binding MyBackgroundUrl}" ImageFailed="ImageBrush_ImageFailed"/>
in the code behind I update the image source property to set it to a local image:
protected void ImageBrush_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var img = sender as ImageBrush;
if (img == null) return;
var uri = new BitmapImage(new Uri("ms-appx:///Assets/App/MyDefaultBackground.jpg"));
img.ImageSource = uri;
}
this works GREAT, however by doing this, I am losing the original binding, so that if I reload the same screen with a DIFFERENT binding value, it doesn't re-bind.
This makes sense given what I'm doing, so in that case my question is what did I do wrong and how can I correctly setup a fallback image while allowing the image to re-bind itself when it reloads?
Of course you are. You are overwriting the binding. You have to set the property like this:
void ImageBrush_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
var img = sender as ImageBrush;
if (img == null) return;
var uri = new BitmapImage(new Uri("ms-appx:///Assets/App/MyDefaultBackground.jpg"));
var vm = img.DataContext as MainPageViewModel;
vm.MyBackgroundUrl = uri;
}
If you don't like that, then you can have two images. One on top of the other and set its visibility based on failure. Something like this pseudo code:
var img = new Image();
// you would not new it up here, you would ref your control.
img.ImageOpened += (s, e) => OverlayImage.Visibility = Visibility.Collapsed;
img.ImageFailed += (s, e) => OverlayImage.Visibility = Visibility.Collapsed;
Make sense to you?
I just noticed you are talking about an image brush and not an image. The logic should be the same, but instead of an overlay it might need to be an underlay. Also, because it is an underlay, you might not need to toggle its visibility since the successful image brush will occlude the underlay. Might be even easier in your case.
Best of luck!
I'm using geoTools 14.1
I'm trying to plot on an image some points
This is the code I'm using:
double[][] points = new double[8][2];
points[0] = new double[]{45.46433710338643, 9.190417528152478};
points[1] = new double[]{45.46195085146914, 9.189746320685355};
points[2] = new double[]{45.460062304163635, 9.19015527826191};
points[3] = new double[]{45.472950871127445, 9.17363731952788};
points[4] = new double[]{45.4737153001908,9.203728795018847};
points[5] = new double[]{45.4849795331724,9.20162835217198};
points[6] = new double[]{45.48560542313713,9.195953607559215};
points[7] = new double[]{45.48348421787171,9.188765287399292};
final SimpleFeatureType TYPE = DataUtilities.createType("Location",
"location:Point:srid=3857,"+// <- the geometry attribute: Point type
"nome:String," + // <- a String attribute
"id:Integer" // a number attribute
);
FeatureCollection<SimpleFeatureType, SimpleFeature> collection = new DefaultFeatureCollection();
GeometryFactory geometryFactory = JTSFactoryFinder.getGeometryFactory(null);
SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(TYPE);
CoordinateReferenceSystem pointSrc = CRS.decode("EPSG:4326");
CoordinateReferenceSystem targetCRS = CRS.decode("EPSG:3857");
MathTransform transform = CRS.findMathTransform(pointSrc, targetCRS);
for (int i = 0; i < points.length; i++)
{
double[] coords = points[i];
Point point = geometryFactory.createPoint(new Coordinate(coords[0], coords[1]));
Point converted = (Point) JTS.transform( point, transform);
featureBuilder.add(converted);
featureBuilder.add("Punto "+i);
featureBuilder.add(i);
SimpleFeature feature = featureBuilder.buildFeature(""+i);
logger.info(""+feature+" feature.getDefaultGeometry() "+feature.getDefaultGeometry()+ " feature.getDefaultGeometryProperty() "+feature.getDefaultGeometryProperty());
((DefaultFeatureCollection)collection).add(feature);
}
String wellKnownName = "Circle";
Style style = SLD.createPointStyle(wellKnownName,Color.RED,Color.RED,0f,10f);
FeatureLayer fl = new FeatureLayer(collection, style);
fl.setVisible(true);
fl.setSelected(true);
logger.info(""+fl);
mapcontent.addLayer((org.geotools.map.Layer)fl); //"1010177.1917802,5688070.7096562,1029133.5747922,5704122.4855938"
ReferencedEnvelope bounds = new ReferencedEnvelope(1010177.1917802,1029133.5747922,5688070.7096562, 5704122.4855938, targetCRS);
BufferedImage ret = buildImage(mapcontent, 5000, 5000, bounds, Color.white);
ImageIO.write((RenderedImage) ret, "png", new File("/home/angelo/Scrivania/immagineResult.png"));
It seems to me all correct, but the generated image contains no point.
This is the generated image
As you can see it's all white; I was expecting only 8 red circles on the image... Is there any error in my code? Am I doing anything wrong?
Thank you
Angelo
UPDATE: added build image method
public BufferedImage buildImage(final MapContent map, final int imageWidth,final int imageHeight,ReferencedEnvelope bounds,Color bgcolor) {
GTRenderer renderer = new StreamingRenderer();
renderer.setMapContent(map);
renderer.setMapContent(map);
Rectangle imageBounds = null;
ReferencedEnvelope mapBounds = bounds;
try {
if(bounds==null) mapBounds = map.getMaxBounds();
imageBounds = new Rectangle(imageWidth, imageHeight);
} catch (Exception e) {
failed to access map layers
throw new RuntimeException(e);
}
BufferedImage image = new BufferedImage(imageBounds.width, imageBounds.height, BufferedImage.TYPE_4BYTE_ABGR);
Graphics2D gr = image.createGraphics();
int type = AlphaComposite.SRC;
gr.setComposite(AlphaComposite.getInstance(type));
Color c = new Color(bgcolor.getRed(), bgcolor.getGreen(), bgcolor.getBlue(), 0);
gr.setBackground(bgcolor);
gr.setColor(c);
gr.fillRect(0, 0, image.getWidth(), image.getHeight());
type = AlphaComposite.SRC_OVER;
gr.setComposite(AlphaComposite.getInstance(type));
try {
renderer.paint(gr, imageBounds, bounds);
} catch (Exception e) {
throw new RuntimeException(e);
}
return image;
}
After a lot of playing I have found the problem :-) There is no bug in your code! There are in fact 8 red dots in that white image, but they are very hard to find!
As the above image shows at 2000% zoom (and panning along the top edge) you will find a dot (I'm assuming the others are there) - the simple answer is either to make the dots 10 times bigger (100px) or the image much smaller (500x500) and in both cases the dots are immediately visible.
I have code that is supposed to find the shortest path from point A to point B. To do this i am using a A-star variation. I am using a 2d array to represent a 2d grid but my path does not take diagonal shortcuts, only left, right, up, and down. So far everything works fine except it does not always find the shortest path possible. I want to know what is going wrong, why it is going wrong, and how I can fix it. Thank you in advance.
Here is a picture to illustrate what exactly is happening:
and here is my code (path finding class first, then its helper class):
BTW: Math vector is nothing more than just a geometric point class, and both playerTileLocation and enemyTileLocation are just points that correspond to the start and end nodes on the grid. Also i use the class AStarNode as the nodes for all the tiles on the map, instead of a regular object.
package {
import src.Characters.Character;
import src.InGame.Map;
import src.Maths.MathVector;
public final class BaseAI {
// REPRESENTS UP, DOWN, RIGHT, AND LEFT OF ANY ONE NODE
private static const bordersOfNode:Array = new Array(
new MathVector( -1, 0), new MathVector(1, 0), new MathVector(0, -1), new MathVector(0, 1));
private var _player:Character;
private var map:Map;
private var playerTileLocation:MathVector;
private var openList:Array;
private var closedList:Array;
// 2D ARRAY OF MAP TILES (I DON'T USE HERE, BUT I PLAN TO IN FUTURE)
private var mapArray:Array;
private var originNode:AStarNode;
private var complete:Boolean;
public function BaseAI(_player:Character,map:Map):void {
this._player = _player;
this.map = map;
openList = new Array();
closedList = new Array();
mapArray = map.tiles;
}
public function get player():Character {
return this._player;
}
public function calculatePlayerTileLocation():void {
playerTileLocation = map.worldToTilePoint(player.groundPosition);
}
//WILL EVENTUAL RETURN A DIRECTION FOR THE ENEMY TO TAKE THAT ITERATION (EVERY 1-2 SECONDS)
public function getDirection(enemy:Character):String {
var enemyTileLocation:MathVector = map.worldToTilePoint(enemy.groundPosition);
originNode = new AStarNode(enemyTileLocation, playerTileLocation);
originNode.setAsOrigin();
openList = [originNode];
closedList = [];
complete = false;
var currentNode:AStarNode;
var examiningNode:AStarNode;
while (!complete) {
openList.sortOn("F", Array.NUMERIC);
currentNode = openList[0];
closedList.push(currentNode);
openList.splice(0, 1);
for (var i in bordersOfNode) {
examiningNode = new AStarNode(new MathVector(currentNode.X + bordersOfNode[i].x, currentNode.Y + bordersOfNode[i].y),playerTileLocation);
if (map.isOpenTile(map.getTile(examiningNode.X, examiningNode.Y)) && !examiningNode.isThisInArray(closedList)) {
if (!examiningNode.isThisInArray(openList)) {
openList.push(examiningNode);
examiningNode.parentNode = currentNode;
}else {
}
if (examiningNode.X == playerTileLocation.x && examiningNode.Y == playerTileLocation.y) {
complete = true;
var done:Boolean = false;
var thisNode:AStarNode;
thisNode = examiningNode;
while (!done) {
if (thisNode.checkIfOrigin()) {
done = true;
}else {
thisNode = thisNode.parentNode;
}
}
}
}
}
}
}
}
}
package {
import src.Maths.MathVector;
internal final class AStarNode {
private var _X:int;
private var _Y:int;
private var _G:int;
private var _H:int;
private var _F:int;
private var _parentNode:AStarNode;
private var _isOrigin:Boolean;
public static const VERTICAL:uint = 10;
public function AStarNode(thisNodeLocation:MathVector, targetNodeLocation:MathVector) {
X = thisNodeLocation.x;
Y = thisNodeLocation.y;
H = Math.abs(X - targetNodeLocation.x) + Math.abs(Y - targetNodeLocation.y);
G = 0;
F = H + G;
}
public function set X(newX:int):void {
this._X = newX;
}
public function get X():int {
return this._X;
}
public function set Y(newY:int):void {
this._Y = newY;
}
public function get Y():int {
return this._Y;
}
public function set G(newG:int):void {
this._G = newG;
}
public function get G():int {
return this._G;
}
public function set H(newH:int):void {
this._H = newH;
}
public function get H():int {
return this._H;
}
public function set F(newF:int):void {
this._F = newF;
}
public function get F():int {
return this._F;
}
public function set parentNode(newParentNode:AStarNode):void {
this._parentNode = newParentNode;
}
public function get parentNode():AStarNode {
return this._parentNode;
}
public function setAsOrigin():void {
_isOrigin = true;
}
public function checkIfOrigin():Boolean {
return _isOrigin;
}
public function isThisInArray(arrayToCheck:Array):Boolean {
for (var i in arrayToCheck) {
if (arrayToCheck[i].X == this.X && arrayToCheck[i].Y == this.Y) {
return true
}
}
return false
}
}
enter code here
}
A quick glance through your code raises the idea of wrong heuristics. Your G value is always 0 in a node, at lease I do not see where it could change. However, in A-star algorithm for your task (finding the shortest path with obstacles) it should represent the number of steps already made to reach the cell. That would allow the algorithm to replace the long path with a shorter one.
The one time I coded an A star 'algorithm' I used a 2-dimensional Array for the grid (as you have). At the start of the search each grid location's 'searched' property was set to false. Each grid location would also have an Array of connecting directions; options that the player could choose to move in - some might be open, some might be blocked and inaccessible.
I would start the search by checking the starting grid position for how many direction options it had. For each option I would push a 'path' Array into a _paths Array. Each 'path' Array would end up containing a sequence of 'moves' (0 for up, 1 for right, 2 for down and 3 for left). So for each initial path, I would push in the corresponding starting move. I would also set the grid position's 'searched' property to true.
I would then iterate through each path, running through that sequence of moves to get to the most recently added location. I would check if that location was the target location. If not I would mark that location as searched then check which directions were available, ignoring locations that had already been searched. If non were available, the path would be closed and 'spliced' from the Array of paths.
Otherwise ByteArray 'deep copies' of the current path Array were made for each available move option, in excess of the first move option. A move in one direction was added to the current path and the new paths, in their respective directions.
If the number of paths ever reaches 0, there is not a path between the 2 locations.
I think that was about it. I hope that's helpful.
Note that the search does not need to be 'directed' toward the target; what I've suggested searches all possible paths and just 'happens' to find the most direct route by killing paths that try to check locations that have already been searched (meaning some other path has got there first and is therefore shorter).
I am working on a Windows Phone 8 app. I am using a Storyboard to animate a series of images. It is working fine, but I want to call a particular method one second before the animation completes. I am using this code: Is there a way to do what I want?
var storyboard12 = new Storyboard
{
// RepeatBehavior = RepeatBehavior.Forever
};
var animation = new ObjectAnimationUsingKeyFrames();
Storyboard.SetTarget(animation, animationImage);
Storyboard.SetTargetProperty(animation, new PropertyPath("Source"));
storyboard12.Children.Add(animation);
for (int i = 1; i <=16; i++)
{
var keyframe = new DiscreteObjectKeyFrame
{
KeyTime = KeyTime.FromTimeSpan(TimeSpan.FromMilliseconds(300*i)),
Value = String.Format("/Images+Audio/images/animation images/2_Driving-a-car/Drive_background3 ({0}).png", i)
};
animation.KeyFrames.Add(keyframe);
}
DispatcherTimer timer11 = new DispatcherTimer();
timer11.Interval = TimeSpan.FromSeconds(4.1);
timer11.Tick += timer11_Tick;
timer11.Start();
storyboard12.Begin();
storyboard12.Completed += storyboard12_Completed;
}
void timer11_Tick(object sender, EventArgs e)
{
var timer = (DispatcherTimer)sender;
timer.Stop();
changeBackgroundImage3();
}
You can use an another Storyboard, that will change a background of your control. Such way is more appropriated by a performance reason. And DispatcherTimer Tick event won't be 100% fired after set interval, it could be fired later than 4.1, if it is important for you then it is the second reason to use a storyboard to change a background.
This is in conjunction to the question posed here:
JTabbedPane: Components before and after the tabs themselves
I want to attach a mouse listener that allows for dragging the constructed google chrome-like frame. Starting out, the initial dragging code is rather easy, and the mouse-dragging code at this Kirill-post can be used pretty much directly. I'd only want this behaviour if the user clicks and drags on the "title bar" of the frame, that is, the area where the tabs (the stick-uppers) reside. This is also easy - just change the dragging code to only accept clicks in the upper area of the JTabbedPane, the part that contains the tabs.
However, I want to reduce the grabbable area further, and only allow click-and-drag-frame in the area NOT occupied by the tabs (the stick-uppers - anyone have a better name for this GUI element?) - again quite like Google Chrome (Chrome also adds a bar above the tabs when in windowed mode, to easier get hold of the frame if many tabs are active. But Chrome do this perfect: It is possible to grab the window in the tabbed part that doesn't have tabs, and even in the small v's inbetween the tabs!)
What I'd effectively want to do, is to be able to attach the mouse listeners to the background of the GUI for the tabs - but how to accomplish something like this?
After looking at the solutions for this question (draggable tabs in Swing), I found that the actual TabbedPaneUI has some methods that can fix both problems: Only drag window in tab area, which turned out to be the hardest part, and not drag when above the tabs themselves. The relevant code follows, in the two sections marked with "// ::". The code is adapted from the question-mentioned Kirill-code. The code doesn't handle other cases than when the tabs are at top - which makes sense when considering what I want to do.
// mouse listener for dragging the host window
MouseAdapter adapter = new MouseAdapter() {
int lastX;
int lastY;
boolean _dragInitiated;
#Override
public void mousePressed(MouseEvent e) {
TabbedPaneUI ui = _windowTabs.getUI();
// :: Won't drag if we're positioned above a tab in tab area
if (ui.tabForCoordinate(_windowTabs, e.getX(), e.getY()) != -1) {
_dragInitiated = false;
return;
}
// :: Won't drag if we're below the tab area
int maxY = 0;
for (int i = 0; i < _windowTabs.getTabCount(); i++) {
Rectangle bounds = ui.getTabBounds(_windowTabs, i);
int y = bounds.y + bounds.height;
if (y > maxY) {
maxY = y;
}
}
_dragInitiated = true;
if (maxY > 0) {
if (e.getY() > maxY) {
_dragInitiated = false;
}
}
Point eventLocationOnScreen = e.getLocationOnScreen();
if (eventLocationOnScreen == null) {
Component source = (Component) e.getSource();
eventLocationOnScreen = new Point(e.getX() + source.getLocationOnScreen().x, e.getY()
+ source.getLocationOnScreen().y);
}
lastX = eventLocationOnScreen.x;
lastY = eventLocationOnScreen.y;
}
#Override
public void mouseDragged(MouseEvent e) {
if (!_dragInitiated) {
return;
}
Point eventLocationOnScreen = e.getLocationOnScreen();
if (eventLocationOnScreen == null) {
Component source = (Component) e.getSource();
eventLocationOnScreen = new Point(e.getX() + source.getLocationOnScreen().x, e.getY()
+ source.getLocationOnScreen().y);
}
int dx = eventLocationOnScreen.x - lastX;
int dy = eventLocationOnScreen.y - lastY;
Window win = POTabbedFrame.this;
Point loc = win.getLocation();
win.setLocation(loc.x + dx, loc.y + dy);
lastX = eventLocationOnScreen.x;
lastY = eventLocationOnScreen.y;
}
};
_windowTabs.addMouseListener(adapter);
_windowTabs.addMouseMotionListener(adapter);