UWP set map icon to not moving when zooming - c#

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 )

Related

Rotating points 90 degrees

I'm building a paint-esque program where the user can draw shapes like rectangles and ellipses on the canvas. These shapes have two coordinates stored, the corner from when the user started to draw it and the corner where the user stopped drawing it. I want to be able to rotate these shapes 90 degrees clockwise around the center of the bitmap they are drawn on, this bitmap is 600 x 600 pixels large. How can I transform the avaliable coordinates of these shapes so that they appear rotated 90 degrees.
This is the code that I have been trying to make work:
foreach (TekeningElement t in getekende_elementen)
{
int x_begin = t.GetBeginPunt.X; //store x coordinate of starting point
int y_begin = t.GetBeginPunt.Y; //store y coordinate of starting point
int x_eind = t.GetEindPunt.X; //store x coordinate of end point
int y_eind = t.GetEindPunt.Y; //store y coordinate of end point
int x_verschil = x_eind - x_begin; //calculate width
int y_verschil = y_eind - y_begin; //calculate height
t.SetBeginPunt = new Point(600 - x_begin - y_verschil, x_begin); //set new starting point
t.SetEindPunt = new Point(600 - x_eind + y_verschil, x_eind); //set new end point
}
Thanks for the help.

AnimateCamera in GoogleMaps is not displaying all location included in builder

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 .

How does one set an image as or along a chart axis?

I am trying to use a colored spectrum strip as an axis for a chart. The idea is to match the color on the image with its associated wavelength along the x-axis at the bottom. The strip needs to change in size to match changes of the chart area and expand and contract sections to match scroll-zooming in the chart area.
I have tried using image annotations but as the chart area changes, the annotation dimensions remain fixed. Also, the scroll zooming that focuses in on mouse position obviously has no effect on the annotation.
The approach that came closest was using the image as a background for the chart area. This automatically scaled the image as the chart area changed but scroll-zooming has no effect on the background image. Also, it would be ideal to have the background clear so as to avoid obscuring data plot points. I can edit the image to have a large transparent section and only a colored strip at the bottom but even then, that strip could obscure lower intensity data points.
Spectrum as annotation and background:
Annotation not scaling, background scales well:
Both annotation and background not scaling with zooming:
This is a nice idea.
The simplest way is to draw the image in a Paint event of the Chart, maybe PrePaint.
Let's go to work.. We will use the DrawImage overload that allows us zooming as well as cropping. For this we need two rectangles.
The first challenge is to always get the correct target rectangle.
For this we need to convert the InnerPlotPosition from relative positions to absolute pixels.
These two functions will help:
RectangleF ChartAreaClientRectangle(Chart chart, ChartArea CA)
{
RectangleF CAR = CA.Position.ToRectangleF();
float pw = chart.ClientSize.Width / 100f;
float ph = chart.ClientSize.Height / 100f;
return new RectangleF(pw * CAR.X, ph * CAR.Y, pw * CAR.Width, ph * CAR.Height);
}
RectangleF InnerPlotPositionClientRectangle(Chart chart, ChartArea CA)
{
RectangleF IPP = CA.InnerPlotPosition.ToRectangleF();
RectangleF CArp = ChartAreaClientRectangle(chart, CA);
float pw = CArp.Width / 100f;
float ph = CArp.Height / 100f;
return new RectangleF(CArp.X + pw * IPP.X, CArp.Y + ph * IPP.Y,
pw * IPP.Width, ph * IPP.Height);
}
With these numbers setting the destination rectangle is as simple as:
Rectangle tgtR = Rectangle.Round(new RectangleF(ipr.Left, ipr.Bottom - 15, ipr.Width, 15));
You can chose a height as you like..
The next challenge is the source rectangle.
Without zooming it would simply be:
Rectangle srcR = new Rectangle( 0, 0, bmp.Width, bmp.Height);
But for zooming and panning we need to scale it; for this we can use the x-axis and the ScaleView's Minimum and Maximum values.
We calculate factors for the first and last spot on the axis:
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
now we get the source rectangle maybe like this:
int x = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
Let's put it together:
private void chart_PrePaint(object sender, ChartPaintEventArgs e)
{
// a few short names
Graphics g = e.ChartGraphics.Graphics;
ChartArea ca = chart.ChartAreas[0];
Axis ax = ca.AxisX;
// pixels of plot area
RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);
// scaled first and last position
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
// actual drawing with the zooming overload
using (Bitmap bmp = (Bitmap)Bitmap.FromFile(imagePath))
{
int x = (int)(bmp.Width * f1);
int xx = (int)(bmp.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, bmp.Height);
Rectangle tgtR = Rectangle.Round(
new RectangleF(ipr.Left , ipr.Bottom - 15, ipr.Width, 15));
g.DrawImage(bmp, tgtR, srcR, GraphicsUnit.Pixel);
}
}
A few notes:
Of course I would recomend to use an Image resource instead of always loading from disk!
The Drawing will always overlay the data points and also the grids. You can either..
choose a different minimum to make room
make the image smaller
move it below the x-axis labels
make the image semi-transparent
make the x-axis so fat that it can hold the image strip : ax.LineWidth = 10
For the latter solution you would want to offset the y-position depending on the zoom state. Quick and dirty: int yoff = (ax.ScaleView.IsZoomed ? 12 : 5);. To avoid black stripes also make the axis Transparent or chart.BackColor..
Update:
You can also revert to using a StripLine. It can scale its BackgroundImage and you would have to create a suitable image whenever changing the scaleview, i.e. when zooming or panning. For this much of the above code would be used to create the new images. See this post for examples of adding and replacing varying NamedImage to a Chart! (The relevant portion is close to the end about the marker images!)
In fact I found that way to be the best solution and have added a second answer.
Alternative and recommended solution:
I dabbled with the last option I mentioned in my other answer and found it to be rather nice; it is similarily extensive, so I decided to post a second answer.
The idea is to use a StripLine with just the right BackgroundImage.
The advantage is that is will display nicely under all chart elements and never draw over the axis, grid, datapoints or conflict with the zoom tools.
Since the StripLine must be updated repeatedly I put it in a function:
Here is the function; it makes use of the same two helper functions to calculate pixel positions as the other answer does..:
void updateStripLine(Chart chart, ChartArea ca, string name)
{
// find our stripline; one could pass in a class level variable as well
StripLine sl = ca.AxisY.StripLines.Cast<StripLine>()
.Where(s => s.Tag.ToString() == name).FirstOrDefault();
if (sl != null) // either clean-up the resources..
{
var oldni = chart.Images.FindByName(name);
if (oldni != null)
{
oldni.Image.Dispose();
chart.Images.Remove(oldni);
oldni.Dispose();
}
}
else // or, create the line
{
sl = new StripLine();
sl.Tag = name;
ca.AxisY.StripLines.Add(sl);
}
ca.RecalculateAxesScale();
RectangleF ipr = InnerPlotPositionClientRectangle(chart, ca);
Axis ax = ca.AxisX;
Axis ay = ca.AxisY;
double f1 = ax.ScaleView.ViewMinimum / (ax.Maximum - ax.Minimum);
double f2 = ax.ScaleView.ViewMaximum / (ax.Maximum - ax.Minimum);
Bitmap b0 = (Bitmap)chart.Images["spectrum"].Image;
int x = (int)(b0.Width * f1);
int xx = (int)(b0.Width * f2);
Rectangle srcR = new Rectangle( x, 0, xx - x, b0.Height);
Rectangle tgtR = Rectangle.Round(new RectangleF(0,0, ipr.Width , 10));
// create bitmap and namedImage:
Bitmap bmp = new Bitmap( tgtR.Width, tgtR.Height);
using (Graphics g = Graphics.FromImage(bmp))
{ g.DrawImage(b0, tgtR, srcR, GraphicsUnit.Pixel); }
NamedImage ni = new NamedImage(name, bmp);
chart.Images.Add(ni);
sl.BackImageWrapMode = ChartImageWrapMode.Scaled;
sl.StripWidth = ay.PixelPositionToValue(0) - ay.PixelPositionToValue(12);
sl.Interval = 100; // make large enough to avoid another sLine showing up
sl.IntervalOffset = 0;
sl.BackImage = name;
}
Much of the comments and links apply, especially wrt to the NamedImage we use for the StripLine.
A few more notes:
I use one of the (four) axis conversion functions, PixelPositionToValue to calculate a pixel height of 12px; the StripLine takes values, so I use two pixel values to get the right difference value.
To identify the StripLine I use the Tag property. Of course the Name property would be much more natural, but it is read-only. No idea why?!
The function is called from the AxisViewChanged, the Resize event and also the the PrePaint event; this makes sure it will always be called when needed. To avoid invalid calls from the PrePaint there I do it like this: if (ay.StripLines.Count == 0) updateStripLine(chart, ca, "sl"); Of course you should adapt if you use other StripLines on this axis..
The code makes use of the same image as before; but I have put it into a first NamedImage called spectrum. This would be an option in the 1st answer as well.
NamedImage spectrum = new NamedImage("spectrum", Bitmap.FromFile(imagePath);
chart.Images.Add(spectrum);
It also makes sure to dispose of the old images properly, I hope..

C# Revit API, CreateIndependentTag method failing to add tag to ceilings, exception at line location 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.

WPF Get Polygon Point Coordinates

I have a WPF polygon which can have variable number of sides. I would like to get the X, Y Coordinates of each intersection dynamically.
I am able to get the Relative Point of the polygon using the below code
Point relativePoint = polygon.TransformToAncestor(LayoutRoot)
.Transform(new Point(0, 0));
If the polygon is square I am able sort of get the coodinates by adding the Height and width to the relative point.
double polygonWidth = polygon.ActualWidth/2;
double polygonHeight = polygon.ActualHeight/2;
But if it is not square how can get all the X,Y cordinates for the intersection.
Given a point in coordinates relative to the Polygon, you may easily check this:
if (polygon.RenderedGeometry.FillContains(point))
{
...
}
If you are going to check that on a mouse click, you can get the relative point by calling
var point = e.GetPosition(polygon);
where e is a MouseButtonEventArgs.

Categories