I am working with GoogleMap and I am trying to focus my map on a region that displayed my list of locations. The list gets "included" into the builder one by one, but when I actually call this method a lot of my locations are cropped out. Seems to me like the zoom level on the NewLatLngBounds is too high.
I thought about getting the Northeast and Southwest Corners and then centering the map but that doesn't work either. Not sure what else to try.
public void DisplayRegion(List<Position> positions, int padding = 0)
{
if (_googleMap == null) throw new InvalidOperationException("Map is not ready");
LatLngBounds.Builder builder = new LatLngBounds.Builder();
foreach(var p in positions)
{
builder.Include(new LatLng(p.Latitude, p.Longitude);
}
LatLngBounds bounds = builder.Build();
//var ne = bounds.Northeast;
// var se = bounds.Southwest;
_googleMap.AnimateCamera(CameraUpdateFactory.NewLatLngBounds(bounds, padding));
}
This does work but like I said above a good chunk of the locations passed in are cropped off screen. I also tried factoring in the width and height of the and basing padding on that but it's like I didn't do anything. All I want is for every item in my List of Positions to display on screen at an appropriate zoom level. Any suggestions?
First , select a location as the center of the map,just get the midpoint from Northeast and Southwest Corners , let's say we get a point ,it has centerLatitude and centerLongitude .
Calculate how far are the positions between the center location , and get the farthest distance , call it farthestDistance.
Use map.MoveToRegion (MapSpan.FromCenterAndRadius (new Position (centerLatitude,centerLongitude), Distance.FromKilometers (farthestDistance)));
Then the map will display including all the positions and use a proper zoom level .
Refer to https://stackoverflow.com/a/53735393/8187800 .
Related
I am working on a project which consists of drawing a line, BUT, the most important thing is that I need to get the Y position on every X position. So, for every X I need a Y. Drawing a line and getting the positions works fine while I draw slowly. But when I move the cursor faster, even if the line gets drawn, the positions are not saved for every X, and this is a problem. You can just take a look at the image below.
As you can see, the green line is the one that Unity provides, LineRenderer. Just to prove my problem, I have drawn a circle at every position saved on the Line. So, even though the line gets drawn, I can't save my positions. Is there a way to solve this? Thank you and have a great day!
If I understood your question correct now you want to find all pixels that lie on the lines build by the given points in an image.
Not fully sure since typing on the smartphone right now but I think you could do something like
// Expects givenPoints in pixel coordinates for positions to draw lines between
public List<Vector2Int> CalculateLinePixels(List<Vector2Int> givenPoints)
{
var output = new List<Vector2Int>();
// Already add the first point once
output.Add(givenPoints[0]);
// start with the second point and calculate the line points back
// between the current and the point before
for (var i = 1; i < givenPoints.Count; i++)
{
var startPoint = givenPoints[i - 1];
var endPoint = givenPoints[i];
// get the difference between them in pixels
var dif = endPoint - startPoint;
// Get the pixel step in Y per pixel step on X
var step = dif.y / (float)dif.x;
// go through all the pixels on the X axis between both points
// excluding the first as these should already match the
// startPoint (== last endPoint)
//
// Note that for now this assumes that the line goes always strict from left to right
// -> no forth and back drawing, no vertical drawing
for (var x = 1; x < dif.x; x++)
{
// every step in X add one step in Y starting from the first points Y
var y = Mathf.RoundToInt(step * x);
// Add the new line point
output.Add(startPoint + new Vector2Int(x, y));
}
}
return output;
}
As also commented in code:
Note that this assumes that the line goes always strict from left to right. => No forth and back drawing, no horizontal drawing.
For these you could add some additional checks such as if(dif.x < 0) then you would need to iterate in the other direction for (var x = -1; x > dif.x ; x--). And if it is if(dif.x == 0) you might want to rather only draw a horizontal line to the next point filling all above/below.
For better results you might want to look into Line drawing algorithms and implement a different way of how to obtain and fill the pixels differently.
If I understood correctly, your problem is caused by the fact that the coordinates are sampled rarely, you could just focus on getting the values while moving the cursor and then draw the line, it would be a faster way.
How to set Map Icon to be not moving when zooming and also directly point to precise location? Because from what I see when the map zooming out, it doesn't point to latitude and longitude I set. But when I zooming in it show to the correct latitude and longitude.
Here is some code I try
var originPoint =
new Geopoint(new BasicGeoposition
{
Latitude = -7.9301346197875446,
Longitude = 112.63243081568352
});
//create POI
var originPin = new MapIcon
{
Location = originPoint,
NormalizedAnchorPoint = new Point(0.5, 0.5),
ZIndex = 0,
Image =
RandomAccessStreamReference.CreateFromUri(
new Uri(
"ms-appx:///mapicon.png"))
};
nativeMap.MapElements.Add(originPin);
According to MSDN Documentation I need to set normalized anchor point. But what is the correct point to set? I already try (0,0),(0.5,1),(0.5,0.5)(1,1) and it still need to be zooming in to show the correct position. Look at my image below
The NormalizedAnchorPoint is the correct item to use in this case... it is a point system with 0,0 being upper left corner. And 1,1 being lower right corner.
Seeing your image, we assume your anchor point is the lower left corner... ( the red dot ) so try (1,0) or (0,1) ( I'm not sure at the top of my head what the x axis is, I would think the first point )
This code attempts to add a tag to all ceilings in the ceiling views list. The ceiling view list populates and I can get the ceiling elements from the document, but it seems to be failing when trying to acquire the centre point of the ceiling element. I have googled all the blogs and I cant find a reference to tagging floors in revit either as it may be a similar scenario.
public IndependentTag CreateIndependentTag(Document doc)
{
List<View> viewList = ceilingViewCollector(doc);
foreach (View view in viewList)
{
// Find all ceiling elements in the document by using category filter
ElementCategoryFilter filter = new ElementCategoryFilter(BuiltInCategory.OST_Ceilings);
// Use shortcut WhereElementIsNotElementType() to find ceiling instances only
FilteredElementCollector collector = new FilteredElementCollector(doc);
IList<Element> CeilingList = collector.WherePasses(filter).WhereElementIsNotElementType().ToElements();
foreach (Ceiling ceiling in CeilingList)
{
TaskDialog.Show("Ceiling", ceiling.Name);
// define tag mode and tag orientation for new tag
TagMode tagMode = TagMode.TM_ADDBY_CATEGORY;
TagOrientation tagorn = TagOrientation.Horizontal;
//add tag to centre of ceiling?
LocationPoint p = ceiling.Location as LocationPoint;
p.Point = new XYZ(0.0, p.Point.Y, p.Point.Z);
ceilingCentre = p.Point;
string coords = "point = " + ceilingCentre.ToString();
TaskDialog.Show("Source room Center", coords);
IndependentTag newTag = doc.Create.NewTag(view, ceiling, true, tagMode, tagorn, ceilingCentre);
if (null == newTag)
{
throw new Exception("Create IndependentTag Failed.");
}
// set leader mode free
// otherwise leader end point move with elbow point
newTag.LeaderEndCondition = LeaderEndCondition.Free;
XYZ elbowPnt = ceilingCentre + new XYZ(5.0, 5.0, 0.0);
newTag.LeaderElbow = elbowPnt;
XYZ headerPnt = ceilingCentre + new XYZ(10.0, 10.0, 0.0);
newTag.TagHeadPosition = headerPnt;
return newTag;
}
}
return null;
}
I'm no expert but, aren't you forgetting your X-component?
//add tag to centre of ceiling?
LocationPoint p = ceiling.Location as LocationPoint;
p.Point = new XYZ(p.Point.X / 2, p.Point.Y / 2, p.Point.Z);
ceilingCentre = p.Point;
It would seem you'd want to center this Point p in terms of X and Y, and keep the Z component.
A little late but I just ran across this. Revit elements can be located by a point (most family instances), a line (walls, line based components), or a sketch (ceilings, floors, etc.). Your code will not work because you are casting the location to a location point and it isn't a location point.
With sketches you can't cast the location to anything useful so you will need to figure out how you want to determine the center of the ceiling on your own. The simple answer is to find the bounding box of the ceiling and calculate it's center as suggested by mtumminello. This will work for most ceilings except if you have a large L shape or something the bounding box center may not be over the ceiling at all. If you need this covered you would have to come up with some other algorithm to find the center point.
I have a simple list of points List<Point> which I populate using the mouse. Like, when I click the mouse, the location is added into the list. The problem is that the new location is added at the bottom of the list. What I want to do is, when I click the mouse, search all the existing points in the list and return the closest point to the mouse location, and insert the new point after that one.
I've been searching the web for hours and I can't seem to find a solution. Any help will be appreciated. Thanks!
The List<> class contains an .Insert() method to do just that. When you search the list and find the "closest" element (however you define that logic), you can get the index of that object in the list. Then just insert the new one after that index. Something like:
var closestPoint = FindClosestPoint(listOfPoints, newPoint);
var index = listOfPoints.IndexOf(closestPoint);
listOfPoints.Insert(index + 1, newPoint);
Getting the closest point itself should be a simple matter of geometry. You have two X/Y coordinates on a plane. The distance between them is the square root of the sum of the squares of the axes. So you just need the element where that value is the smallest. Something like this:
var closestPoint = listOfPoints
.Select(p => new {
Point = p,
Distance = Math.Sqrt(
Math.Pow(Math.Abs(p.X - closestPoint.X), 2) +
Math.Pow(Math.Abs(p.Y - closestPoint.Y), 2)
)
})
.OrderByDesc(p => p.Distance)
.First();
I have decided to have a go at making a dungeon crawler game with the Xna framework. I am a computer science student and am quite familiar with c# and .net framework. I have some questions about different parts of the development for my engine.
Loading Maps
I have a tile class that stores the vector2 position, 2dtexture and dimensions of the tile. I have another class called tilemap that has a list of tiles that are indexed by position. I am reading from a text file which is in the number format above that matches the number to the index in the tile list and creates a new tile with the correct texture and position, storing it into another list of tiles.
public List<Tile> tiles = new List<Tile>(); // List of tiles that I have added to the game
public List<TileRow> testTiles = new List<TileRow>(); // Tilerow contains a list of tiles along the x axis along with there vector2 position.
Reading and storing the map tiles.
using (StreamReader stream = new StreamReader("TextFile1.txt"))
{
while (stream.EndOfStream != true)
{
line = stream.ReadLine().Trim(' ');
lineArray = line.Split(' ');
TileRow tileRow = new TileRow();
for (int x = 0; x < lineArray.Length; x++)
{
tileXCo = x * tiles[int.Parse(lineArray[x])].width;
tileYCo = yCo * tiles[int.Parse(lineArray[x])].height;
tileRow.tileList.Add(new Tile(tiles[int.Parse(lineArray[x])].titleTexture, new Vector2(tileXCo,tileYCo)));
}
testTiles.Add(tileRow);
yCo++;
}
}
For drawing the map.
public void Draw(SpriteBatch spriteBatch, GameTime gameTime)
{
foreach (TileRow tes in testTiles)
{
foreach (Tile t in tes.tileList)
{
spriteBatch.Draw(t.titleTexture, t.position, Color.White);
}
}
}
Questions:
Is this the correct way I should be doing it, or should I just be storing a list referencing my tiles list?
How would I deal with Multi Layered Maps?
Collision Detection
At the moment I have a method that is looping through every tile that is stored in my testTiles list and checking to see if its dimensions are intersecting with the players dimensions and then return a list of all the tiles that are. I have a derived class of my tile class called CollisionTile that triggers a collision when the player and that rectangle intersect. (public class CollisionTile : Tile)
public List<Tile> playerArrayPosition(TileMap tileMap)
{
List<Tile> list = new List<Tile>();
foreach (TileRow test in tileMap.testTiles)
{
foreach (Tile t in test.tileList)
{
Rectangle rectangle = new Rectangle((int)tempPosition.X, (int)tempPosition.Y, (int)playerImage.Width / 4, (int)playerImage.Height / 4);
Rectangle rectangle2 = new Rectangle((int)t.position.X, (int)t.position.Y, t.width, t.height);
if (rectangle.Intersects(rectangle2))
{
list.Add(t);
}
}
}
return list;
}
Yeah, I am pretty sure this is not the right way to check for tile collision. Any help would be great.
Sorry for the long post, any help would be much appreciated.
You are right. This is a very inefficient way to draw and check for collision on your tiles. What you should be looking into is a Quadtree data structure.
A quadtree will store your tiles in a manner that will allow you to query your world using a Rectangle, and your quadtree will return all tiles that are contained inside of that Rectangle.
List<Tiles> tiles = Quadtree.GetObjects(rectangle);
This allows you to select only the tiles that need to be processed. For example, when drawing your tiles, you could specify a Rectangle the size of your viewport, and only those tiles would be drawn (culling).
Another example, is you can query the world with your player's Rectangle and only check for collisions on the tiles that are returned for that portion of your world.
For loading your tiles, you may want to consider loading into a two dimensional array, instead of a List. This would allow you to fetch a tile based on its position, instead of cross referencing it between two lists.
Tile[,] tiles = new Tile[,]
Tile tile = tiles[x,y];
Also, in this case, an array data structure would be a lot more efficient than using a List.
For uniform sets of tiles with standard widths and heights, it is quite easy to calculate which tiles are visible on the screen, and to determine which tile(s) your character is overlapping with. Even though I wrote the QuadTree in Jon's answer, I think it's overkill for this. Generally, the formula is:
tileX = someXCoordinate % tileWidth;
tileY = someYCoordinate % tileHeight;
Then you can just look that up in a 2D array tiles[tileX, tileY]. For drawing, this can be used to figure out which tile is in the upper left corner of the screen, then either do the same again for the bottom right (+1), or add tiles to the upper left to fill the screen. Then your loop will look more like:
leftmostTile = screenX % tileWidth; // screenX is the left edge of the screen in world coords
topmostTile = screenY % tileHeight;
rightmostTile = (screenX + screenWidth) % tileWidth;
bottommostTile = (screenY + screenHeight) % tileHeight;
for(int tileX = leftmostTile; tileX <= rightmostTile; tileX++)
{
for(int tileY = topmostTile; tileY <= bottommostTile; tileY++)
{
Tile t = tiles[tileX][tileY];
// ... more stuff
}
}
The same simple formula can be used to quickly figure out which tile(s) are under rectangular areas.
IF however, your tiles are non-uniform, or you have an isometric view, or you want the additional functionality that a QuadTree provides, I would consider Jon's answer and make use of a QuadTree. I would try to keep tiles out of the QuadTree if you can though.