MonoTouch: Get latitude and longitude values crashes - xamarin.ios

In my app i want to get the latitude and longitude values. I have below code. It crashes very first time the function get called. App asks the user permission to use current location and app crashes throwing null reference exception where i am assigning latitude value to a string
string getLocation()
{
string latlong = "|";
CLLocationManager locationManager = new CLLocationManager();
locationManager.StartUpdatingLocation();
//var locationVar = locationManager.Location.Coordinate;
string lat = locationManager.Location.Coordinate.Latitude.ToString();
string lon = locationManager.Location.Coordinate.Longitude.ToString();
latlong = lat + "|" + lon;
return latlong;
}

You cannot retrieve the location from locationManager until it has retrieved a location. You need to assign an event handler that will be called when the location is found.
The CoreLocation sample from Xamarin includes a complete example of how to do this
// handle the updated location method and update the UI
if (UIDevice.CurrentDevice.CheckSystemVersion (6, 0)) {
iPhoneLocationManager.LocationsUpdated += (object sender, CLLocationsUpdatedEventArgs e) => {
UpdateLocation (mainScreen, e.Locations [e.Locations.Length - 1]);
};
} else {
// this won't be called on iOS 6 (deprecated)
iPhoneLocationManager.UpdatedLocation += (object sender, CLLocationUpdatedEventArgs e) => {
UpdateLocation (mainScreen, e.NewLocation);
};
}

Related

Getting object name and randomly placing it to Text UI - Unity

I am a beginner in Unity and I am currently making a simple game. I have a problem managing the flow of my minigame. The minigame is simply finding an object, when I found and tap on the item name below will be shaded or marked or there can be animation just to indicate that the item is found.
What I want to do is to get the name of the objects that need to be found and set them randomly in the three (3) item names below. like every time this minigame opens the names of the items are randomly placed in the 3 texts. And when the item is found the name below will be marked or shaded or anything that will indicate it is found, but for now I will just set it inactive for easier indication. How can I properly do this whole process?
The objects inside the scene are button for them to have onCLick() events
Correction: the term choices are wrong because they just display the name of the items that you need to find, just in case you get confused with the term choice in my minigame. I will fix it.
Here is the visuals for the minigame:
The script I currently have for when the objects was clicked:
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using TMPro;
public class ClickObject : MonoBehaviour
{
[SerializeField] Button pillowBtn, pcBtn, lampBtn;
// [SerializeField] TextMeshProUGUI choice1, choice2, choice3;
// FROM THE SOLUTION
[SerializeField] List<GameObject> gameObjectWanted;
[SerializeField] List<TextMeshProUGUI> textBoxes;
// METHOD NAMES IS TEMPORARY
public void pillowClicked()
{
Debug.Log("you found the " + EventSystem.current.currentSelectedGameObject.name);
}
public void desktopClicked()
{
Debug.Log("you found the " + EventSystem.current.currentSelectedGameObject.name);
}
public void lampClicked()
{
Debug.Log("you found the " + EventSystem.current.currentSelectedGameObject.name);
}
}
You asked for a lot and I hope I understood your intention.
first, if you want to randomly choose an amount of game objects from your game, I think the best way to do it is by adding the refernece of all the game objects you want to choose from radomly inside a list of game objects and then randomly take a game object of the list and make it a child of another game object I call "fatherGoTranform" on my code like that:
[SerializeField] List<GameObject> gameObjectWanted;
[SerializeField] float numOfGO = 4;
[SerializeField] Transform fatherGoTranform;
void Start()
{
for(int i=0;i<numOfGO;i++)
{
int index = Random.Range(0, gameObjectWanted.Count-1);
GameObject currentGO = gameObjectWanted[index ];
currentGO.transform.parent = fatherGoTranform;
gameObjectWanted.RemoveAt(index);
}
}
and then to click on a game object and the do with what you want try this:
void Update()
{
//Check for mouse click
if (Input.GetMouseButtonDown(0))
{
RaycastHit raycastHit;
Ray ray = Camera.main.ScreenPointToRay(Input.mousePosition);
if (Physics.Raycast(ray, out raycastHit, 100f))
{
if (raycastHit.transform != null)
{
//Our custom method.
CurrentClickedGameObject(raycastHit.transform.gameObject);
}
}
}
}
I have not checked the code so if there is an error tell me and I will fix it
This is the solution that worked for my problem. I randomly placed numbers to the list according to childCount and every index indicate the index of the text that I want to put them on which I get as a Transform child.
using UnityEngine;
using UnityEngine.UI;
using UnityEngine.EventSystems;
using System.Collections.Generic;
using TMPro;
public class ClickObject : MonoBehaviour
{
// [SerializeField] Button pillowBtn, pcBtn, lampBtn;
[SerializeField] GameObject choices, v1, finishPanel;
// RANDOM NUMBER HOLDER FOR CHECKING
private int randomNumber, foundCount;
// RANDOMIZED NUMBER LIST HOLDER
public List<int> RndmList = new List<int>();
private void Awake()
{
foundCount = 0;
RndmList = new List<int>(new int[v1.transform.childCount]);
for (int i = 0; i < v1.transform.childCount; i++)
{
randomNumber = UnityEngine.Random.Range(0, (v1.transform.childCount) + 1);
while (RndmList.Contains(randomNumber))
{
randomNumber = UnityEngine.Random.Range(0, (v1.transform.childCount) + 1);
}
RndmList[i] = randomNumber;
// Debug.Log(v1.transform.GetChild(randomNumber-1).name);
choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().text = v1.transform.GetChild(randomNumber - 1).name;
}
}
public void objectFound()
{
string objectName = EventSystem.current.currentSelectedGameObject.name;
Debug.Log("you found the " + objectName);
for (int i = 0; i < choices.transform.childCount; i++)
{
if (objectName == choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().text)
{
// Debug.Log(i);
// choices.transform.GetChild(i).gameObject.SetActive(false);
// choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().color = Color.gray;
if(RndmList.Contains(i+1))
{
// Debug.Log(i);
// Debug.Log(v1.transform.GetChild(RndmList[i]-1).name);
choices.transform.GetChild(i).GetComponentInChildren<TextMeshProUGUI>().color = Color.gray;
v1.transform.GetChild(RndmList[i]-1).GetComponent<Button>().enabled = false;
foundCount++;
}
}
}
if(foundCount == v1.transform.childCount)
{
finishPanel.SetActive(true);
}
}
}

Azure Digital twins - Twin to Twin update

first of all, I'd like to make it clear I have no experience at all with C#, so it might be the case that this is a very trivial question.
I am trying to follow this tutorial: https://learn.microsoft.com/en-us/azure/digital-twins/how-to-send-twin-to-twin-events
using this function sample: https://github.com/Azure-Samples/azure-digital-twins-getting-started/blob/main/azure-functions/twin-updates/ProcessDTRoutedData.cs
However, I am facing a weird error.
The idea of this code is that every time a child twin is updated somehow, for example with data coming from an IoT HUB, the parent twin would also be updated. For example, we have a room with 2 thermostats, when there is an update on the thermostats the room temperature would be updated with an average of the temperatures of the thermostats.
My issue is the following:
if my thermostat 1 has a value of 25oC and thermostat 2 has a value of 27oC at timestep 1 my room temperature should be 26oC, however, I am getting no data, I only get data on the parent twin on timestep 2, when a new update is done on the thermostats, so if at the timestep 2 thermostat 1 has a temperature of 30oC and thermostat 2, a temp of 32oC, the room should show 31oC, however, it shows the 26oC from timestep 1, as you can see we keep having this delayed reaction.
to be a bit more specific on my case, I have a device that aggregates (sums) readings from other 4 devices, the total on the aggregator looks off by one iteration everytime this function is called
// Default URL for triggering event grid function in the local environment.
// http://localhost:7071/runtime/webhooks/EventGrid?functionName={functionname}
using IoTHubTrigger = Microsoft.Azure.WebJobs.EventHubTriggerAttribute;
using Azure;
using Azure.Core.Pipeline;
using Azure.DigitalTwins.Core;
using Azure.Identity;
using Microsoft.Azure.EventGrid.Models;
using Microsoft.Azure.WebJobs;
using Microsoft.Azure.WebJobs.Extensions.EventGrid;
using Microsoft.Extensions.Logging;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using System;
using System.Net.Http;
using System.Linq;
using System.Reflection.Metadata.Ecma335;
using System.Threading.Tasks;
using System.Collections.Generic;
using TwinUpdatesSample.Dto;
namespace TwinUpdatesSample
{
public class ProcessDTRoutedData
{
private static HttpClient _httpClient = new HttpClient();
private static string _adtServiceUrl = Environment.GetEnvironmentVariable("ADT_SERVICE_URL");
/// <summary>
/// The outcome of this function is to get the average floor temperature and humidity values based on the rooms on that floor.
///
/// 1) Get the incoming relationship of the room. This will get the floor twin ID
/// 2) Get a list of all the rooms on the floor and get the humidity and temperature properties for each
/// 3) Calculate the average temperature and humidity across all the rooms
/// 4) Update the temperature and humidity properties on the floor
/// </summary>
/// <param name="eventGridEvent"></param>
/// <param name="log"></param>
/// <returns></returns>
[FunctionName("ProcessDTRoutedData")]
public async Task Run([EventGridTrigger] EventGridEvent eventGridEvent, ILogger log)
{
log.LogInformation("ProcessDTRoutedData (Start)...");
DigitalTwinsClient client;
DefaultAzureCredential credentials;
// if no Azure Digital Twins service URL, log error and exit method
if (_adtServiceUrl == null)
{
log.LogError("Application setting \"ADT_SERVICE_URL\" not set");
return;
}
try
{
//Authenticate with Azure Digital Twins
credentials = new DefaultAzureCredential();
client = new DigitalTwinsClient(new Uri(_adtServiceUrl), credentials, new DigitalTwinsClientOptions { Transport = new HttpClientTransport(_httpClient) });
}
catch (Exception ex)
{
log.LogError($"Exception: {ex.Message}");
client = null;
credentials = null;
return;
}
if (client != null)
{
if (eventGridEvent != null && eventGridEvent.Data != null)
{
JObject message = (JObject)JsonConvert.DeserializeObject(eventGridEvent.Data.ToString());
log.LogInformation($"A state change in the Helix Building DT has been detected");
//log.LogInformation($"Message: {message}");
string twinId = eventGridEvent.Subject.ToString();
log.LogInformation($"TwinId: {twinId}");
string modelId = message["data"]["modelId"].ToString();
log.LogInformation($"ModelId: {modelId}");
string smartPlugAggregatorId = null;
if (modelId.Contains("dtmi:digitaltwins:rec_3_3:core:logicalDevice:SmartPlug;1"))
{
log.LogInformation($"Updating ProjectSmartPlug state with new state from {twinId}");
// logicalDevice SmartPlug should always report to a logicalDevice SmartPlug Aggregator;
// go get the sourceId for the logicalDevice SmartPlug Aggregator the logicalDevice SmartPlug is related to
AsyncPageable<IncomingRelationship> smartPlugAggregatorList = client.GetIncomingRelationshipsAsync(twinId);
// get the sourceId (parentId)
await foreach (IncomingRelationship smartPlugAggregator in smartPlugAggregatorList)
if (smartPlugAggregator.RelationshipName == "observes")
{
smartPlugAggregatorId = smartPlugAggregator.SourceId;
}
log.LogInformation($"{smartPlugAggregatorId} observes to {twinId} for change in state during this iteration");
// if the parentId (SourceId) is null or empty, then something went wrong
if (string.IsNullOrEmpty(smartPlugAggregatorId))
{
log.LogError($"'SourceId' for observes relationship is missing from GetIncomingRelationships({twinId}) call. This should never happen.");
return;
}
AsyncPageable<BasicDigitalTwin> queryResponse = client.QueryAsync<BasicDigitalTwin>($"SELECT smartPlug FROM digitaltwins smartPlugAggregator JOIN smartPlug RELATED smartPlugAggregator.observes WHERE smartPlugAggregator.$dtId = '{smartPlugAggregatorId}'");
List<SmartPlug> SmartPlugList = new List<SmartPlug>();
// loop through each smartPlugSensor and build a list of smartPlugSensors
await foreach(BasicDigitalTwin twin in queryResponse)
{
JObject smartPlugPayload = (JObject)JsonConvert.DeserializeObject(twin.Contents["smartPlug"].ToString());
log.LogInformation($"Smart Plug {twin.Id} payload: {smartPlugPayload}");
SmartPlugList.Add(new SmartPlug() {
id = twin.Id,
ActiveEnergyWh = Convert.ToDouble(smartPlugPayload["ActiveEnergyWh"]),
ActivePowerW = Convert.ToDouble(smartPlugPayload["ActivePowerW"]),
ReActiveEnergyVARh = Convert.ToDouble(smartPlugPayload["ReActiveEnergyVARh"]),
ReActivePowerVAR = Convert.ToDouble(smartPlugPayload["ReActivePowerVAR"]),
});
}
// if no rooms, then something went wrong and method should exit
if (SmartPlugList.Count < 1)
{
log.LogError($"'roomList' is empty for floor ({smartPlugAggregatorId}). This should never happen.");
return;
}
// get the sum from the list of smartPlug Logical Devices
double sumActiveEnergyWh = SmartPlugList.Sum(x => x.ActiveEnergyWh);
log.LogInformation($"Sum ActiveEnergyWh : {sumActiveEnergyWh.ToString()}");
double sumActivePowerW = SmartPlugList.Sum(x => x.ActivePowerW);
log.LogInformation($"Sum ActivePowerW : {sumActivePowerW.ToString()}");
double sumReActiveEnergyVARh = SmartPlugList.Sum(x => x.ReActiveEnergyVARh);
log.LogInformation($"Sum ReActiveEnergyVARh : {sumReActiveEnergyVARh.ToString()}");
double sumReActivePowerVAR = SmartPlugList.Sum(x => x.ReActivePowerVAR);
log.LogInformation($"Sum ReActivePowerVAR : {sumReActivePowerVAR.ToString()}");
var updateTwinData = new JsonPatchDocument();
updateTwinData.AppendReplace("/ActiveEnergyWh", Math.Round(sumActiveEnergyWh, 2));
updateTwinData.AppendReplace("/ActivePowerW", Math.Round(sumActivePowerW, 2));
try
{
log.LogInformation(updateTwinData.ToString());
await client.UpdateDigitalTwinAsync(smartPlugAggregatorId, updateTwinData);
log.LogInformation("ProcessDTRoutedData (Done)...");
log.LogInformation(" ");
}
catch (Exception ex)
{
log.LogError($"Error: {ex.Message}");
}
return;
}
}
}
}
}
}
As your query is making a projection (i.e. specifying one or more columns you want to return, as opposed to doing a SELECT *), the query returns an AsyncPageable<IDictionary<string, BasicDigitalTwin>> (as opposed to AsyncPageable<BasicDigitalTwin> for wildcard queries).
You are looking at changing your code to:
AsyncPageable<IDictionary<string, BasicDigitalTwin>> queryResponse = client.QueryAsync<IDictionary<string, BasicDigitalTwin>>($"SELECT smartPlug FROM digitaltwins smartPlugAggregator JOIN smartPlug RELATED smartPlugAggregator.observes WHERE smartPlugAggregator.$dtId = '{smartPlugAggregatorId}'");
List<SmartPlug> SmartPlugList = new List<SmartPlug>();
// loop through each smartPlugSensor and build a list of smartPlugSensors
await foreach(IDictionary<string, BasicDigitalTwin> d in queryResponse)
{
if (d.ContainsKey("smartPlug")) {
SmartPlugList.Add(JsonConvert.DeserializeObject<SmartPlug>(d["smartPlug"].ToString()));
}
}
Please see https://learn.microsoft.com/en-us/azure/digital-twins/how-to-query-graph#run-queries-with-the-api for more info.

I tried to get address from latitude and longitude through list<address> but got IndexOutOfBoundsException every time

tried to get the address using latitude and longitude. Same code works on other devices though, address list returns null on some devices(physical)
android version: 11.
MainActivity.java
public void onLocationResult(#NonNull LocationResult locationResult) {
super.onLocationResult(locationResult);
LocationServices.getFusedLocationProviderClient(MainActivity.this)
.removeLocationUpdates(this);
if (locationResult != null && locationResult.getLocations().size() >0){
int index = locationResult.getLocations().size() - 1;
double latitude = locationResult.getLocations().get(index).getLatitude();
double longitude = locationResult.getLocations().get(index).getLongitude();
geocoder = new Geocoder(MainActivity.this, Locale.getDefault());
try {
addresses = geocoder.getFromLocation(latitude, longitude, 1); // Here 1 represent max location result to returned, by documents it recommended 1 to 5
} catch (IOException e) {
e.printStackTrace();
}
String address = addresses.get(0).getAddressLine(0);
String city = addresses.get(0).getLocality();
String state = addresses.get(0).getAdminArea();
String country = addresses.get(0).getCountryName();
AddressText.setText("address: "+ address + "\n" + "city: "+ city + "state: "+ state + "\n" + "country: "+ country);
}
}
Error:
2022-05-25 09:36:53.805 29773-29773/com.example.location E/AndroidRuntime: FATAL EXCEPTION: main
Process: com.example.location, PID: 29773
java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
at java.util.ArrayList.get(ArrayList.java:437)
at com.example.location.MainActivity$2.onLocationResult(MainActivity.java:142)
at com.google.android.gms.internal.location.zzap.notifyListener(com.google.android.gms:play-services-location##18.0.0:2)
at com.google.android.gms.common.api.internal.ListenerHolder.zaa(com.google.android.gms:play-services-base##18.0.1:2)
at com.google.android.gms.common.api.internal.zacb.run(Unknown Source:4)
at android.os.Handler.handleCallback(Handler.java:938)
at android.os.Handler.dispatchMessage(Handler.java:99)
at android.os.Looper.loop(Looper.java:233)
at android.app.ActivityThread.main(ActivityThread.java:8010)
at java.lang.reflect.Method.invoke(Native Method)
at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:631)
at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:978)
Geocoder is not stable and sometimes stops working, when Geocoder breaks it simply returns an empty list. Unfortunately, there is no way to check if Geocoder is working in advance. The workaround is to connect to the Google Maps web api when your Geocoder returns an empty list. Look at this post for an example of the code.

CLLocationManager and CLGeoCoder

I want to use coordinate of the actual location (CLLocationManager) to reverse geocoding (CLGeoCoder).
I have this code:
locationMgr = new CLLocationManager();
locationMgr.DesiredAccuracy = CLLocation.AccuracyNearestTenMeters;
locationMgr.DistanceFilter = 10;
locationMgr.UpdatedLocation += (object sender, CLLocationUpdatedEventArgs e) => {
Task.latitude = e.NewLocation.Coordinate.Latitude;
Task.longitude = e.NewLocation.Coordinate.Longitude;
locationMgr.StopUpdatingLocation();
};
btnLocation = new UIBarButtonItem(UIImage.FromFile("Icons/no-gps.png"), UIBarButtonItemStyle.Plain, (s,e) => {
if (CLLocationManager.LocationServicesEnabled) {
locationMgr.StartUpdatingLocation();
geoCoder = new CLGeocoder();
geoCoder.ReverseGeocodeLocation(new CLLocation(Task.latitude, Task.longitude), (CLPlacemark[] place, NSError error) => {
adr = place[0].Name+"\n"+place[0].Locality+"\n"+place[0].Country;
Utils.ShowAlert(XmlParse.LocalText("Poloha"), Task.latitude.ToString()+"\n"+Task.longitude.ToString()+"\n\n"+adr);
});
}
else {
Utils.ShowAlert(XmlParse.LocalText("PolohVypnut"));
}
});
Because UpdatedLocation() take some seconds, input of ReverseGeocodeLocation() is Task.latitude=0 and Task.longitude=0.
How can I wait for right values (Task.latitude, Task.longitude) before ReverseGoecodeLocation()?
Thanks for any help.
Your geocoder's ReverseGeocodeLocation method is called before the CLLocationManager gets a location.
Calling StartUpdatingLocation does not mean that the UpdatedLocation event will be triggered immediately. Furthermore, if you are on iOS 6, UpdatedLocation will never be triggered. Use the LocationsUpdated event instead.
Example:
locationManager.LocationsUpdated += (sender, args) => {
// Last item in the array is the latest location
CLLocation latestLocation = args.Locations[args.Locations.Length - 1];
geoCoder = new CLGeocoder();
geoCoder.ReverseGeocodeLocation(latestLocation, (pl, er) => {
// Read placemarks here
});
};
locationManager.StartUpdatingLocation();

LWUIT4IO (v1.5) ConnectionRequest's readResponse() Issue - Nokia SDK 2.0

I have been porting an existing J2ME mobile app, that allows users to view archived news videos, to the latest Nokia SDK 2.0 platform for Series 40 full-touch devices.
I am using both the LWUIT and LWUIT4IO technologies for the UI and Network functionalities of the application respectively.
The app has been tested to work on the S40 5th Edition SDK platform emulator. Extending LWUIT4IO's ConnectionRequest class and utilizing LWUIT's XMLParser, the app can successfully send a HTTP request and get the expected response data from a web service that basically returns an XML-formatted type of feed (containing necessary metadata for the video) (Here's the URL of the web service: http://nokiamusic.myxph.com/nokianewsfeed.aspx?format=3gp)
But for some reason, this is not the case when trying to run the app on the latest Nokia SDK 2.0 platform. It throws a java.lang.NullPointerException upon trying to parse (XMLParser.parse()) the InputStream response of the web service. When I trace the Network Traffic Monitor of the emulator of the corresponding Request sent and Response received - 0 bytes were returned as content despite a successful response status 200. Apparently the XMLParser object has nothing to parse in the first place.
I am hoping that you can somehow shed light on this issue or share any related resolutions, or help me further refine the problem.
Posted below is the code of the SegmentService class (a sub-class of LWUIT's ConnectionRequest) that connects to the webservice and processes the XML response:
public class SegmentService extends ConnectionRequest implements ParserCallback {
private Vector segments;
private Video segment;
public SegmentService(String backend) {
String slash = backend.endsWith("/") ? "" : "/";
setPost(false);
setUrl(backend + slash + "nokianewsfeed.aspx");
addArgument("format", "3gp");
}
public void setDateFilter(String date) {
System.out.println(date);
addArgument("date", date);
}
private Video getCurrent() {
if (segment == null) {
segment = new Video();
}
return segment;
}
protected void readResponse(InputStream input) throws IOException {
InputStreamReader i = new InputStreamReader(input, "UTF-8");
XMLParser xmlparser = new XMLParser();
System.out.println("Parsing the xml...");
Element element = xmlparser.parse(i);
System.out.println("Root " + element.getTagName());
int max = element.getNumChildren();
System.out.println("Number of children: " + max);
segments = new Vector();
for (int c = 0; c < max; c++) {
Element e = element.getChildAt(c);
System.out.println("segment " + c);
int len = e.getNumChildren();
System.out.println("Number of children: " + len);
for (int d=0; d<len; d++) {
Element s = e.getChildAt(d);
String property = s.getTagName();
System.out.println("key: " + property);
String value = (s.getNumChildren()>0) ? s.getChildAt(0).getText() : null;
System.out.println("value: " + value);
if (property.equals("title")) {
getCurrent().setTitle(value);
} else if (property.equals("description")) {
getCurrent().setDescription(value);
} else if (property.equals("videourl")) {
getCurrent().setVideoUrl(value);
} else if (property.equals("thumburl")) {
getCurrent().setThumbUrl(value);
} else if (property.equals("adurl")) {
getCurrent().setAdUrl(value);
} else if (property.equals("publishdate")) {
getCurrent().setPublishDate(value);
} else if (property.equals("category")) {
getCurrent().setCategory(value);
} else if (property.equals("weburl")) {
getCurrent().setWebUrl(value);
} else if (property.equals("thumburl2")) {
getCurrent().setThumb210(value);
} else if (property.equals("thumburl4")) {
getCurrent().setThumb40(value);
}
}
if (segment != null) {
segments.addElement(segment);
segment = null;
}
}
fireResponseListener(new NetworkEvent(this, segments));
}
public boolean parsingError(int errorId, String tag, String attribute, String value, String description) {
System.out.println(errorId);
System.out.println(tag);
System.out.println(value);
System.out.println(description);
return true;
}
}

Resources