|
POST
|
I have a Xamarin Forms app with using a custom LocationDataSource receiving data from an external gps unit. Things seem to work in terms of getting data and updates being seen in the appropriate event handlers. However, the display symbol location does not update as I walk around. Occasionally, it will jump back over to my location but in general it just stays at the location when I opened the map. When I go out and come back into to a view (which means a new MapView would be created, I will be at the correct location which is further evidence that I am receiving the correct location data. I setup something to flip back to using the SystemLocationDataSource and when this is done the indicator follows as expected. Is there something I am missing that would need to be triggered in a custom LocationDataSource so that the MapView is refreshed? [I did have an application running in WPF which used basically the identical LocationDataSource - although the source of the stream was different - and in this case had the correct behavior) Thanks -Joe
... View more
08-30-2019
12:43 PM
|
0
|
2
|
1753
|
|
POST
|
For single line address just use the overload that accepts a string LocatorTask.GeocodeAsync Method (String)
... View more
08-30-2019
05:45 AM
|
0
|
0
|
1511
|
|
DOC
|
Tom, Does this support fittings with differing inlet and outlet diameter as per ASTM standard? Thanks -Joe
... View more
08-28-2019
08:29 AM
|
0
|
0
|
41784
|
|
POST
|
I assume it is connected service table. There is a limit to the number of features that will be returned which can be changed on the server. Not sure why javascript would behave differently
... View more
08-26-2019
08:18 AM
|
0
|
0
|
1735
|
|
POST
|
Seems like a lot of complicated code. I have never seen the behavior you describe, and have never had to track Draw Status. Simply have a ViewModel attached to the view with MapView with a bound Map and open the package and set the Map property. Don't even have to do anything with the binding because I just use the Prism naming convention. You definitely 100% do not want to be inheriting your view model from MapView. I have changed things around a bit because I created a shared MapView control in order to Incorporate custom behavior and use in different views in multiple modules <?xml version="1.0" encoding="utf-8" ?>
<esri:MapView xmlns="http://xamarin.com/schemas/2014/forms"
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
xmlns:prism="clr-namespace:Prism.Mvvm;assembly=Prism.Forms"
xmlns:esri="clr-namespace:Esri.ArcGISRuntime.Xamarin.Forms;assembly=Esri.ArcGISRuntime.Xamarin.Forms"
xmlns:mapView="clr-namespace:Mobile.AsBuilt.Framework.MapView;assembly=Mobile.AsBuilt.Framework"
prism:ViewModelLocator.AutowireViewModel="True"
x:Class="Mobile.AsBuilt.Framework.MapView.MapViewControl"
x:Name="MapView"
Map="{Binding Map}"
SketchEditor="{Binding SketchEditor}"
GraphicsOverlays="{Binding GraphicsOverlays}"
GeoViewTapped="MapViewControl_OnGeoViewTapped"
GeoViewDoubleTapped="MapViewControl_OnGeoViewDoubleTapped"
GeoViewHolding="MapViewControl_OnGeoViewHolding"
ViewpointChanged="MapViewControl_OnViewpointChanged"
NavigationCompleted="MapViewControl_OnNavigationCompleted">
</esri:MapView> Because of the control approach what I do is open the map package in a different view model and then just kick it to the MapViewControl view model via an event. Previously when the MapView was in the main view model I just set the Map property in the OnItialized event public class MapViewControlViewModel : BindableBase
{
#region Private Fields
private IEventAggregator _eventAggregator;
private ILocationProvider _locationProvider;
private ILogger _log;
private Map _map;
private SketchEditor _sketchEditor;
#endregion
public MapViewControlViewModel(IEventAggregator eventAggregator, ILogManager logManager, ILocationProvider locationProvider)
{
//TODO: Setup with subscription token
eventAggregator.GetEvent<MapLoadedEvent>().Subscribe(OnMapLoaded);
eventAggregator.GetEvent<NavigatedFromEvent>().Subscribe(OnNavigatedFrom);
eventAggregator.GetEvent<AddGraphicsOverlayEvent>().Subscribe(OnAddGraphicsOverlay);
EventAggregator = eventAggregator;
}
private void OnAddGraphicsOverlay(GraphicsOverlay go)
{
if (!GraphicsOverlays.Contains(go))
{
GraphicsOverlays.Add(go);
}
}
public IEventAggregator EventAggregator
{
get => _eventAggregator;
set => SetProperty(ref _eventAggregator, value);
}
public ILogger Log
{
get => _log;
set => SetProperty(ref _log, value);
}
public ILocationProvider LocationProvider
{
get => _locationProvider;
set => SetProperty(ref _locationProvider, value);
}
public GraphicsOverlayCollection GraphicsOverlays { get; set; } = new GraphicsOverlayCollection();
public SketchEditor SketchEditor
{
get => _sketchEditor;
set => SetProperty(ref _sketchEditor, value);
}
public Map Map
{
get => _map;
set => SetProperty(ref _map, value);
}
private void OnMapLoaded(MapLoadedEventArgs obj)
{
try
{
Map = obj.Map;
EventAggregator.GetEvent<SketchEditorInitializedEvent>().Publish(SketchEditor);
}
catch (Exception e)
{
Log.Error(e.Message, e);
}
}
private void OnNavigatedFrom()
{
Map = null;
SketchEditor = null;
GraphicsOverlays.Clear();
EventAggregator.GetEvent<MapLoadedEvent>().Unsubscribe(OnMapLoaded);
EventAggregator.GetEvent<AddGraphicsOverlayEvent>().Unsubscribe(OnAddGraphicsOverlay);
}
}
... View more
08-26-2019
06:57 AM
|
0
|
0
|
4985
|
|
BLOG
|
We needed a solution to connect to an external high accuracy GPS from an iOS application being developed in Xamarin Forms. As it was being done through Collector I knew there there had to be a solution out there. But I was having a hard time figuring out how the devices (non-BLE bluetooth device) connected to iOS. I finally started to find some information on Stack Overflow that pointed me towards using EAAccessoryManager.SharedAccessoryManager to get the connected devices. Some other searching got me to this post How to use external gps location data which went into detail on an iOS specific solution. In order to do in Forms and then use from a .Net Standard library a little more plumbing is required than the straight iOS solution. First an interface is needed that will be developed in the main shared assembly. I kept this is simple, and also thought in terms of a pure iOS implementation and not a pluigin that could be used cross platform public interface IAccessorySessionController
{
event EventHandler<NmeaSentenceEventArgs> OnNmeaSentenceReceived;
IEnumerable<string> AvailableAccessories { get; }
Task OpenSession(string protocal);
bool CloseSession();
} This interface was then implemented in the iOS project. The main piece is the OpenSession method. This is where the one connects to the device and starts getting data. public Task OpenSession(string protocal)
{
try
{
EAAccessory connectedAccessory = null;
/* Get list of ConnectedAccessories
//These must be defined as UISupportedExternalAccessoryProtocols in Info.plist */
foreach (var accessory in EAAccessoryManager.SharedAccessoryManager.ConnectedAccessories)
{
if (accessory.ProtocolStrings.Contains(protocal))
{
connectedAccessory = accessory;
break;
}
}
if ( connectedAccessory == null ) return Task.FromResult(false);
connectedAccessory.Disconnected += ConnectedAccessoryOnDisconnected;
if ( _session != null ) return Task.FromResult(true);
try
{
//protacal here also must be UISupportedExternalAccessoryProtocols
_session = new EASession(connectedAccessory, protocal);
}
catch (Exception e)
{
Console.WriteLine(e);
return Task.FromResult(false);
}
//Set delegate for callback
_session.InputStream.Delegate = this;
_session.InputStream.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session.InputStream.Open(); //start receiving data
//not writing in this case but still best to open
_session.OutputStream.Delegate = this;
_session.OutputStream.Schedule(NSRunLoop.Current, NSRunLoopMode.Default);
_session.OutputStream.Open();
return Task.FromResult(true);
}
catch (Exception)
{
return Task.FromResult(false);
}
} Not being an iOS developer I didn't really understand the part about needing the protocols in info.plist and spent a good amount of time banging my head on the table as to why I was not seeing the devices I knew were connected <key>UISupportedExternalAccessoryProtocols</key>
<array>
<string>com.comany.device</string>
</array> Then it's just a matter of handling the callback and parsing the data... public override void HandleEvent(NSStream theStream, NSStreamEvent streamEvent)
{
Console.WriteLine("Arrived HandleEvent");
switch (streamEvent)
{
case NSStreamEvent.None:
Console.WriteLine("StreamEventNone");
break;
case NSStreamEvent.HasBytesAvailable:
Console.WriteLine("StreamEventHasBytesAvailable");
ReadData();
break;
case NSStreamEvent.HasSpaceAvailable:
Console.WriteLine("StreamEventHasSpaceAvailable");
// Do write operations to the device here
break;
case NSStreamEvent.OpenCompleted:
Console.WriteLine("StreamEventOpenCompleted");
break;
case NSStreamEvent.ErrorOccurred:
Console.WriteLine("StreamEventErroOccurred");
break;
case NSStreamEvent.EndEncountered:
Console.WriteLine("StreamEventEndEncountered");
break;
default:
Console.WriteLine("Stream present but no event");
break;
}
}
private void ReadData()
{
uint size = 128;
byte[] bytesReceived = new byte[size];
_result = string.Empty;
while ( _session.InputStream.HasBytesAvailable() )
{
var bytes = _session.InputStream.Read(bytesReceived, size);
_result += Encoding.ASCII.GetString(bytesReceived, 0, (int)bytes);
if ( bytes < 10 ) continue;
string[] lines = _result.Split(new[] { "\n" }, StringSplitOptions.RemoveEmptyEntries);
for (var index = 0; index < lines.Length - 1; index++)
{
var line = lines[index];
OnNmeaSentenceReceived(new NmeaSentenceEventArgs(line));
}
_result = lines.Last();
}
} The NmeaSentenceReceived event is used to forward on the nmea sentence back to the custom LocationDataSource. Once all that was done it was a almost trivial matter to implement the LocationDataSource in the shared project. public class ExternalGpsLocationDataSource : LocationDataSource
{
private MapPoint _mapPoint;
private double _velocity, _course, _accuracy;
private readonly IAccessorySessionController _accessorySessionController;
public ExternalGpsLocationDataSource (IAccessorySessionController accessorySessionController)
{
_accessorySessionController = accessorySessionController;
}
protected override Task OnStartAsync()
{
_accessorySessionController.NmeaSentenceReceived += OnNmeaSentenceReceived;
return _accessorySessionController.OpenSession("com.company.protocal");
}
protected override Task OnStopAsync()
{
_accessorySessionController.CloseSession();
return Task.FromResult(true);
}
private void OnNmeaSentenceReceived(object sender, NmeaSentenceEventArgs e)
{
var line = e.NmeaSentence;
try
{
line = line.TrimEnd('\n', '\r');
var message = NmeaMessage.Parse(line);
ReadLocationAttributesFromMessage(message);
}
catch (Exception)
{
//
}
if (_mapPoint == null || double.IsNaN(_mapPoint.X) || double.IsNaN(_mapPoint.Y))
{
UpdateLastKnownLocation();
return;
}
UpdateLocation(new Esri.ArcGISRuntime.Location.Location(_mapPoint, _accuracy, _velocity, _course, false));
}
private void ReadLocationAttributesFromMessage(NmeaMessage message)
{
switch (message)
{
case Gpgga gpgga:
_mapPoint = new MapPoint(gpgga.Longitude, gpgga.Latitude, SpatialReference.Create(4326));
break;
case Gpgsa gpgsa:
break;
case Gprmc gprmc:
_velocity = double.IsNaN(gprmc.Speed) ? 0 : gprmc.Speed * 0.5144; //knots to m\s
_course = double.IsNaN(gprmc.Course) ? 0 : gprmc.Course;
_mapPoint = new MapPoint(gprmc.Longitude, gprmc.Latitude, SpatialReference.Create(4326));
break;
case Gpgsv gpgsv:
break;
case Gpgst gpgst:
double latError = gpgst.SigmaLatitudeError;
double lonError = gpgst.SigmaLongitudeError;
_accuracy = Math.Sqrt(0.5 * (Math.Pow(latError, 2) + Math.Pow(lonError, 2)));
break;
}
}
private void UpdateLastKnownLocation()
{
//If latest point not valid send previous point as LastKnown
if (_mapPoint == null)
{
_mapPoint = new MapPoint(double.NaN, double.NaN, SpatialReferences.Wgs84);
}
UpdateLocation(new Esri.ArcGISRuntime.Location.Location(_mapPoint, _accuracy, _velocity, _course, true));
}
} I use a NMEA parser that Morten Nielsen wrote so no need to spend any time on the parsing GitHub - dotMorten/NmeaParser: Library for handling NMEA message in Windows Desktop, Store, Phone, Universal, and Xamari… Beyond that it is just replacing the DataSource property on the MapView:LocationDisplay to the custom LocationDataSource and setting Enabled = true; Cheers!
... View more
08-23-2019
11:45 AM
|
2
|
0
|
1518
|
|
POST
|
Morten Nielsen showed in this thread Popups in ArcGIS Runtime SDK for .NET how to make changes in Xaml
... View more
08-22-2019
08:30 AM
|
0
|
0
|
2910
|
|
POST
|
If the external source has 'taken over' as the default provider the DataSource it still the SystemLocationDataSource (.net). I was finally able to get what I needed thanks to all the info at Shimin Cai had posted about using the EAAccessory & NSStreamDelegate. Takes some extra stuff to wire to work with Xamarin Forms but it is all working now
... View more
08-21-2019
04:04 PM
|
2
|
0
|
3125
|
|
POST
|
Was something on my end hanging up the application which blocked the thread. Thanks for all the information you posted, this was key to getting me working
... View more
08-21-2019
03:55 PM
|
0
|
1
|
3125
|
|
POST
|
I was finally able to get a handle on your posted code which was really helpful. I am doing in Xamarin so things need to be architected differently because of how you need to access platfom dependent code from the shared projects. I am pretty close but for one issue. I call Open on the InputStream but my Handle method only gets a called a few times, seems like something is going out of scope but I am holding onto references. Hoping I can figure it out soon, for the few cycles everything goes through the data is being read and everything looks good from the MapView side Thanks -Joe
... View more
08-21-2019
01:25 PM
|
0
|
0
|
3125
|
|
POST
|
Sounds like an interesting approach. I am working almost exclusively in Xamarin these days but if I get back to any WPF stuff will have to take a look Cheers
... View more
08-21-2019
09:50 AM
|
0
|
0
|
2021
|
|
POST
|
Shimin Cai Off topic, but how are you connecting to your device? We have an external bluetooth device and it just does not seem to be recognized using standard approach. Wondering if you did something different Thanks
... View more
08-20-2019
07:44 AM
|
0
|
4
|
4023
|
|
POST
|
Divesh Goyal I am working on an app using an external GPS, it's in Xamarin but I assume things would work the same. What I am trying to figure out is how would I know which is the source of the location data. I can check the accuracy which should give me an idea, but I don't see anything in from either native iOS or from Xamarin that would tell me what the provider is. Looking at Collector as an example it gives you the option to connect to a location provider. I would think based on that, that there is a way to detect the connected location sources. Again, though I can just not find where that is. I have played around with using some bluetooth libraries without luck. I have written a custom LocationDataSource in WPF using the same device we are trying to use in iOS In that case I was able to directly connect to the bluetooth device and capture data. Just does not seem to be working in iOS Any thoughts? Nicholas Furness
... View more
08-20-2019
07:41 AM
|
0
|
2
|
3125
|
|
POST
|
If you want to do something like ITool you basically need to roll your own. In ArcGIS Runtime you are building the user interface from scratch, not integrating into another user interface framework. ITool is tricky because of the on/off behavior. In Runtime this is more complex because you then have be capturing MapView events or in this case using SketchEditor We developed an approach that was more around doing an ICommand using an ItemsControl in Xaml which defines the DataTemplate of your button. Along these lines <ItemsControl Margin="0, 0, 10, 0" x:Name="ToolbarCommands" HorizontalAlignment="Stretch" ItemsSource="{Binding ToolbarCommands}">
<ItemsControl.ItemsPanel>
<ItemsPanelTemplate>
<StackPanel Orientation="Horizontal"></StackPanel>
</ItemsPanelTemplate>
</ItemsControl.ItemsPanel>
<ItemsControl.ItemTemplate>
<DataTemplate>
<Button Padding="10" Style="{DynamicResource ToolbarButtonStyle}" Command="{Binding Command}"
CommandParameter="{Binding}">
<Button.ToolTip>
<StackPanel Background="LightYellow" Width="180">
<Label Content="{Binding Name}" FontWeight="Bold" Margin="0 0 0 5"/>
<TextBlock Text="{Binding ToolTip}" TextWrapping="Wrap" />
</StackPanel>
</Button.ToolTip>
<Image>
<Image.Style>
<Style TargetType="{x:Type Image}">
<Setter Property="Source" Value="{Binding ImageUri}"/>
</Style>
</Image.Style>
</Image>
</Button>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl> Then you can have your own tool interface (in this case IToolbarCommand) namespace Mobile.Framework.UI
{
public interface IToolbarCommand
{
/// <summary>
/// Tool name
/// </summary>
string Name { get; set; }
/// <summary>
/// Detailed tooltip describing tool usage
/// </summary>
string ToolTip { get; set; }
/// <summary>
/// The order (from left to right) that tool will be placed
/// </summary>
int Order { get; set; }
/// <summary>
/// Uri of the image to display on the ToolbarCommand
/// </summary>
Uri ImageUri { get; set; }
/// <summary>
/// The ICommand that is invoked by the ToolbarCommand
/// </summary>
ICommand Command { get; set; }
}
} Then the trick is to just come-up with the way to load these at runtime that fits your application architecture. Our approach was to use tools defined in Prism Modules which fired an event that was received in the main user interface bound view model. Unfortunately for things that had an On./Off state never really defined as clean an approach. What we generally did was have a custom ToggleButton style that had pressed/unpressed look with a bound IsChecked property. The initial click would trigger an ICommand and when it was time to give control back any handlers were released and the IsChecked set back to false. SketchEditor is nice because it is awaitable so things can often be done in a single method. Good luck
... View more
08-19-2019
12:51 PM
|
2
|
0
|
2021
|
|
POST
|
Just a guess but it might be that you are calling both job.Start() and job.GetResultsAsync() which is causing two different threads to try and sync the deltas at the same time. If you are using GetResultsAsync approach you do not need to call job.Start(). GetResultsAsync simplifies processing of the job by wrapping everything in an async operation for you so you don't need to monitor the job. If you do use job.Start() then you actually need to add your own job.ProgressChanged handler and handle the job completion in the event handler. I don't know if the API has anything internally to check if a job object has kicked off a job so that it does not fire the same job request twice
... View more
08-12-2019
12:01 PM
|
1
|
1
|
2025
|
| Title | Kudos | Posted |
|---|---|---|
| 1 | 05-11-2026 09:07 AM | |
| 1 | 10-23-2025 12:16 PM | |
| 1 | 10-19-2022 01:08 PM | |
| 1 | 09-03-2025 09:25 AM | |
| 1 | 04-16-2025 12:37 PM |
| Online Status |
Offline
|
| Date Last Visited |
4 weeks ago
|