I' m trying to keep track of a walk using google maps API in kotlin.
For now I get only the last known current location ( which actually is not the real current one)
This is mine MapsActivity code:
import android.content.pm.PackageManager
import android.location.Location
import android.os.Bundle
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import com.example.macc_project.R
import com.google.android.gms.location.FusedLocationProviderClient
import com.google.android.gms.location.LocationServices
import com.google.android.gms.maps.CameraUpdateFactory
import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.SupportMapFragment
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
class MapsActivity : AppCompatActivity(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
override fun onMarkerClick(p0: Marker?): Boolean {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
}
companion object {
private const val LOCATION_PERMISSION_REQUEST_CODE = 1
}
private lateinit var map: GoogleMap
private lateinit var lastLocation: Location
private lateinit var fusedLocationClient: FusedLocationProviderClient
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_maps_google)
// Obtain the SupportMapFragment and get notified when the map is ready to be used.
val mapFragment = supportFragmentManager
.findFragmentById(R.id.map) as SupportMapFragment
mapFragment.getMapAsync(this)
fusedLocationClient = LocationServices.getFusedLocationProviderClient(this)
}
/**
* Manipulates the map once available.
* This callback is triggered when the map is ready to be used.
* This is where we can add markers or lines, add listeners or move the camera. In this case,
* we just add a marker near Sydney, Australia.
* If Google Play services is not installed on the device, the user will be prompted to install
* it inside the SupportMapFragment. This method will only be triggered once the user has
* installed Google Play services and returned to the app.
*/
override fun onMapReady(googleMap: GoogleMap) {
map = googleMap
map.uiSettings.isZoomControlsEnabled = true
map.setOnMarkerClickListener(this)
setUpMap()
}
private fun setUpMap() {
if (ActivityCompat.checkSelfPermission(this,
android.Manifest.permission.ACCESS_FINE_LOCATION) != PackageManager.PERMISSION_GRANTED) {
ActivityCompat.requestPermissions(this,
arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION), LOCATION_PERMISSION_REQUEST_CODE)
return
}
// 1
map.isMyLocationEnabled = true
// 2
fusedLocationClient.lastLocation.addOnSuccessListener(this) { location ->
// Got last known location. In some rare situations this can be null.
// 3
if (location != null) {
lastLocation = location
val currentLatLng = LatLng(location.latitude, location.longitude)
map.animateCamera(CameraUpdateFactory.newLatLngZoom(currentLatLng, 12f))
}
}
}
}
How can I keep track of the entire walk that I do? For example painting a blue line that follows my walk, from an initial state to a final one
Related
I'm using fusedlocationclient to get the current user location, but somehow the lasLocation is null.
Here's my Location Fragment:
package com.example.atry.MakeComplaint
import Retrofit.INodeJS
import Retrofit.Observables
import Retrofit.RetrofitClient
import android.app.Activity
import android.content.ContentValues.TAG
import android.content.Context
import android.content.Context.*
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageManager
import android.location.*
import android.net.Uri
import android.os.Bundle
import android.util.Log
import androidx.fragment.app.Fragment
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.google.android.gms.maps.MapView
import android.widget.LinearLayout
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getSystemService
import com.ashiqur.weatherapp.utils.GPSUtils
import com.example.atry.R
import com.google.android.gms.common.GooglePlayServicesNotAvailableException
import com.google.android.gms.common.api.ResolvableApiException
import com.google.android.gms.dynamic.SupportFragmentWrapper
import com.google.android.gms.location.*
import com.google.android.gms.maps.*
import com.google.android.gms.maps.model.*
import io.reactivex.disposables.CompositeDisposable
import kotlinx.android.synthetic.main.fragment_complaint_details.view.*
import kotlinx.android.synthetic.main.fragment_location.*
import kotlinx.android.synthetic.main.fragment_location.view.*
import retrofit2.Call
import retrofit2.Response
import java.io.IOException
import java.util.Observer
import java.util.Optional.empty
import java.util.jar.Manifest
import javax.security.auth.callback.Callback
class LocationFragment : Fragment(), OnMapReadyCallback, GoogleMap.OnMarkerClickListener {
override fun onMarkerClick(p0: Marker?)= false
private lateinit var map: GoogleMap
private lateinit var mapView : MapView
lateinit var myAPI: INodeJS
var MyCategory: Observables.ComplaintType?=null
private var listener: OnLocationFragmentInteractionListener? = null
var objectComplaint =
Observables.Complaint(
1 , "dummy problem" ,
"url" ,
Observables.Location("99","99"),
Observables.ComplaintType("Smell", "null"),
Observables.Status(2 , "Unresolved")
)
//for updating user's location/ for current location
private lateinit var fusedLocationClient: FusedLocationProviderClient
private lateinit var locationRequest: LocationRequest
private lateinit var locationCallback: LocationCallback
private lateinit var lastLocation: Location
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val permissions = arrayOf(android.Manifest.permission.ACCESS_FINE_LOCATION, android.Manifest.permission.ACCESS_COARSE_LOCATION)
requestPermissions( permissions,0)
getLocationUpdates()
//INIT API
val retrofit = RetrofitClient.instanc
myAPI = retrofit.create(INodeJS::class.java)
}
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
): View? {
// Inflate the layout for this fragment
val v = inflater.inflate(com.example.atry.R.layout.fragment_location, container, false)
mapView = v.findViewById(R.id.maps)
mapView.onCreate(savedInstanceState)
mapView.onResume()
try {
MapsInitializer.initialize(getActivity()!!.getApplicationContext());
} catch (sendEx: IntentSender.SendIntentException) {
sendEx.printStackTrace();
}
mapView.getMapAsync(this)
v.backToList.setOnClickListener {
backFragment()
}
v.forwardToDescription.setOnClickListener{
getAllData()
}
return v
}
/**
* call this method in onCreate
* onLocationResult call when location is changed
*/
private fun getLocationUpdates() {
//with fusedLocationClient
fusedLocationClient = LocationServices.getFusedLocationProviderClient(context!!)
locationRequest = LocationRequest()
locationRequest.interval = 1000
locationRequest.fastestInterval = 5000
locationRequest.smallestDisplacement = 170f // 170 m = 0.1 mile
locationRequest.priority = LocationRequest.PRIORITY_HIGH_ACCURACY //set according to your app function
locationCallback = object : LocationCallback() {
override fun onLocationResult(locationResult: LocationResult?) {
locationResult ?: return
if (locationResult.locations.isNotEmpty()) {
// get latest location
lastLocation = locationResult.lastLocation
Log.d("lastlocation",lastLocation.toString())
// use your location object
// get latitude , longitude and other info from this
}
}
}
}
//Places the marker on the map and changes its style.
//start location updates
private fun startLocationUpdates() {
fusedLocationClient.requestLocationUpdates(
locationRequest,
locationCallback,
null /* Looper */
)
}
// stop location updates
private fun stopLocationUpdates() {
fusedLocationClient.removeLocationUpdates(locationCallback)
}
// start receiving location update when activity visible/foreground
override fun onResume() {
super.onResume()
mapView.onResume()
startLocationUpdates()
}
// stop receiving location update when activity not visible/foreground
override fun onPause() {
super.onPause()
mapView.onPause()
stopLocationUpdates()
}
override fun onDestroy() {
super.onDestroy();
mapView.onDestroy();
}
override public fun onLowMemory() {
super.onLowMemory();
mapView.onLowMemory()
}
override fun onMapReady(googleMap: GoogleMap?) {
map = googleMap!!
map.uiSettings?.isZoomControlsEnabled = true
map.isMyLocationEnabled = true
val marker = MarkerOptions().position(LatLng(lastLocation.latitude,lastLocation.longitude)).title("hello maps")
marker.icon(BitmapDescriptorFactory.defaultMarker(BitmapDescriptorFactory.HUE_RED))
map.addMarker(marker)
val cameraPosition = CameraPosition.builder().target(LatLng(lastLocation.latitude,lastLocation.longitude)).zoom(12f).build()
map.animateCamera(CameraUpdateFactory.newCameraPosition(cameraPosition))
}
private fun backFragment() {
val manager = (context as AppCompatActivity).supportFragmentManager
manager.popBackStackImmediate()
}
}
Also, if someone could just get this working by using location manager or something else I would really appreciate it. Or maybe a link or video where I could follow.
I've got a slight problem, I'm writing a gps tracking app to track several objects at once. The data comes in over a serial interface, this is coming in fine from what I can tell. The issue is that I need to continually update the JPanel where the map is created and displayed.
public JPanel mapDisplay(){
JPanel mapPanel = new JPanel();
mapPanel.setSize(560, 540);
Coordinate start = new Coordinate (-34.9286, 138.6);
trackMap.addMapMarker(new MapMarkerDot(1Lat, 1Lon));
trackMap.setDisplayPosition(start,8);
System.out.println(1Lat);
mapPanel.add(trackMap);
mapPanel.setVisible(true);
return mapPanel;
}
This is what I have and it's happy to display the point once but won't update. If I print out the 1Lat variable in the serial method it continually prints, however it only does it once here.
A lot of the answers I've found refer to setting markers by arrays, however that won't work in this case as the objects I'm tracking could be anywhere.
Any help would be greatly appreciated :)
Is it possible to use a worker thread and not use an ArrayList? I would run the risk of missing data if I do.
Not necessarily. In a SwingWorker, your implementation of the doInBackground() method can publish() results as they become available. Note in particular that "Results from multiple invocations of publish() are often accumulated for a single invocation of process()." In your process(), simply loop through the List<Coordinate>, update the route and repaint() the map.
import java.awt.Color;
import java.awt.Dimension;
import java.awt.EventQueue;
import java.awt.event.MouseEvent;
import java.util.ArrayList;
import java.util.List;
import javax.swing.JFrame;
import javax.swing.SwingWorker;
import org.openstreetmap.gui.jmapviewer.Coordinate;
import org.openstreetmap.gui.jmapviewer.JMapViewer;
import org.openstreetmap.gui.jmapviewer.MapPolygonImpl;
/**
* #see http://stackoverflow.com/a/37193636/230513
*/
public class MapWorkerTest {
private final List<Coordinate> route = new ArrayList<>();
private void display() {
JFrame f = new JFrame("MapWorker");
f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
JMapViewer map = new JMapViewer() {
#Override
public Dimension getPreferredSize() {
return new Dimension(640, 480);
}
#Override
public String getToolTipText(MouseEvent e) {
Coordinate c = (Coordinate) getPosition(e.getX(), e.getY());
return c.getLat() + " " + c.getLon();
}
};
map.setToolTipText("");
Coordinate start = new Coordinate(-34.9286, 138.6);
route.add(start);
MapPolygonImpl poly = new MapPolygonImpl(route);
poly.setColor(Color.blue);
map.addMapPolygon(poly);
map.setDisplayPosition(start, 10);
f.add(map);
f.pack();
f.setLocationRelativeTo(null);
f.setVisible(true);
new MapWorker(map, start).execute();
}
private class MapWorker extends SwingWorker<Void, Coordinate> {
private final JMapViewer map;
private Coordinate last;
public MapWorker(JMapViewer map, Coordinate start) {
this.map = map;
this.last = start;
}
#Override
protected Void doInBackground() throws Exception {
while (!isCancelled()) {
last = new Coordinate(last.getLat() + 0.0025, last.getLon() + 0.01);
publish(last);
Thread.sleep(1000);
}
return null;
}
#Override
protected void process(List<Coordinate> chunks) {
for (Coordinate c : chunks) {
route.add(c);
}
map.repaint();
}
}
public static void main(String[] args) {
EventQueue.invokeLater(new MapWorkerTest()::display);
}
}
Multiple route management left as a exercise.
Just working on a class that deals with sound files in ActionScript for my Starling project. I would like your opinions on the implementation and whether it would perform well. (This file will be getting huge with embedded files).
This is another thing, if it is embedded, is this a bad thing? I mean if I embedded so many voice files, even though they weren't going to be used would this affect performance?
Anyway here is my implementation:
package assets
{
import flash.media.Sound;
import flash.media.SoundChannel;
import flash.media.SoundTransform;
import flash.utils.Dictionary;
/**
* ...
* #author Shaun Stone
*/
public class CharacterDialogueSoundAssets
{
// This will return the voice file is exists
private static var _soundAssetsDictionary:Dictionary= new Dictionary();
[Embed(source = "../../../media/sounds/dialogue/voice_file_1.mp3")]
public static const VOICE_FILE_1:Class;
/**
* Get voice file from dictionary
*
* #param name
* #return
*/
public static function getVoiceFile(name:String):Sound
{
if (_soundAssetsDictionary[name] == undefined)
{
var voiceFile:Sound = new CharacterDialogueSoundAssets.name() as Sound;
_soundAssetsDictionary[name] = voiceFile;
}
return _soundAssetsDictionary[name];
}
public static function disposeOfVoiceFile(name:String):void
{
if (_soundAssetsDictionary[name] == undefined)
{
return;
}
//dispose for garbage collection
_soundAssetsDictionary[name] = undefined;
}
}
}
My map code seems to cause my app to flicker/flash about every second. Anyone see anything that sticks out? It tracks where I have been with red circles. It doesn't seem to go to the zoom level I set either.
Thanks
package com.direction.investor.farmsprayer;
import com.direction.investor.farmsprayer.R;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.LocationSource.OnLocationChangedListener;
import com.google.android.gms.maps.model.Circle;
import com.google.android.gms.maps.model.CircleOptions;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import com.google.android.gms.maps.model.PolygonOptions;
import com.google.android.gms.maps.model.Polyline;
import com.google.android.gms.maps.model.PolylineOptions;
import com.google.android.gms.maps.*;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.os.Bundle;
import android.graphics.Color;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationManager;
import android.location.LocationListener;
public class MainActivity extends Activity implements LocationListener{
Location myLocation;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
// Get back the mutable Circle
Location myLocation;
LocationManager locationmanager = (LocationManager) getSystemService(LOCATION_SERVICE);
Criteria cr = new Criteria();
String provider = locationmanager.getBestProvider(cr, true);
Location location = locationmanager.getLastKnownLocation(provider);
locationmanager.requestLocationUpdates(provider, 200, 0, (LocationListener) this);
}
#SuppressLint("NewApi")
#Override
public void onLocationChanged(Location location) {
// TODO Auto-generated method stub
GoogleMap mMap;
mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
mMap.setMyLocationEnabled(true);
mMap.animateCamera(CameraUpdateFactory.zoomBy(17));
mMap.moveCamera(CameraUpdateFactory.newLatLng((new LatLng(location.getLatitude(), location.getLongitude()))));
CircleOptions circleOptions = new CircleOptions()
.center(new LatLng(location.getLatitude(), location.getLongitude()));
circleOptions.radius(3.048); // In meters
circleOptions.fillColor(0xffff0000);
circleOptions.strokeWidth(0);
mMap.addCircle(circleOptions);
}
In your current code, you are reinitialising the map fragment every time when your onlocationchanged method gets called.
Decalre your map GoogleMap mMap; as a global variable accessible to all methods in your class. Initialise your map fragment only one time whenever your app loads and so move the below code to your onResume() method so that its called only once. Also after setting mMap to your map fragment, do a null check ensure that the fragment is loaded, before doing other map initialisations.
mMap = ((MapFragment) getFragmentManager().findFragmentById(R.id.map)).getMap();
if(mMap!=null) {
mMap.setMapType(GoogleMap.MAP_TYPE_HYBRID);
mMap.setMyLocationEnabled(true);
mMap.animateCamera(CameraUpdateFactory.zoomBy(17));
}
I am developing an AIR for iOS app and I have just this one issue left on my list of bugs to fix. Basically, when a certain window is opened, the user is able to use their current position to search. I am using the Geolocation class for this and it works fine, except for the fact that StatusEvent.STATUS is not firing. The user is prompted to give permission to use their current location, as it should, but when they choose, my StatusEvent handler is never called.
if ( Geolocation.isSupported ) {
var geo:Geolocation = new Geolocation();
geo.addEventListener( StatusEvent.STATUS, this.geolocationStatusHandler );
this.geolocationStatusHandler();
}
protected function geolocationStatusHandler( e:StatusEvent = null ):void{
var geo:Geolocation = new Geolocation();
this.gpsButton.enabled = !geo.muted;
}
The only thing I can think of is that the alert window (which freezes app execution) is opened with new Geolocation(), before I add the event listener. But if that were the case, calling the handler manually wouldn't occur until after the user closed the Alert (it happens at the same time, roughly. A breakpoint there stops the app while the alert is open)
Is there a solution for this? I could always move the prompt to the very beginning of the app, but I personally prefer not being prompted until it is needed.
Details:
ActionScript Mobile project built with AIR 3.6 SDK
Tested on iPad 2, 3, and Mini, all running iOS 6.1.3
Tested in both release and debug modes
Listen for the GeolocationEvent.UPDATE event.
Also, it appears you're manually calling the handler immediately after the listener; then your handler is instantiating a new Geolocation instead of getting GeolocationEvent latitude and longitude.
Example implementation using Google Geocoding API from XpenseIt tutorial:
package
{
import flash.events.Event;
import flash.events.EventDispatcher;
import flash.events.GeolocationEvent;
import flash.sensors.Geolocation;
import mx.rpc.AsyncResponder;
import mx.rpc.AsyncToken;
import mx.rpc.events.FaultEvent;
import mx.rpc.events.ResultEvent;
import mx.rpc.http.HTTPService;
[Event(name="locationUpdate", type="flash.events.Event")]
public class GeolocationUtil extends EventDispatcher
{
protected var geo:Geolocation;
public var updateCount:int;
protected var service:HTTPService = new HTTPService();
public var location:String;
public var longitude:Number;
public var latitude:Number;
public function GeolocationUtil()
{
service.url = "https://maps.googleapis.com/maps/api/geocode/xml";
}
public function geoCodeAddress(address: String):AsyncToken
{
return service.send({address: address, sensor: Geolocation.isSupported});
}
public function getLocation():void
{
if (Geolocation.isSupported)
{
geo = new Geolocation();
geo.setRequestedUpdateInterval(500);
updateCount = 0;
geo.addEventListener(GeolocationEvent.UPDATE, locationUpdateHandler);
}
}
protected function locationUpdateHandler(event:GeolocationEvent):void
{
// Throw away the first location event because it's almost always the last known location, not current location
updateCount++;
if (updateCount == 1) return;
if (event.horizontalAccuracy <= 150)
{
trace("lat:" + event.latitude + " long:" + event.longitude + " horizontalAccuracy:" + event.horizontalAccuracy);
geo.removeEventListener(GeolocationEvent.UPDATE, locationUpdateHandler);
geo = null;
}
longitude = event.longitude;
latitude = event.latitude;
var token:AsyncToken = service.send({latlng: latitude+","+longitude, sensor: Geolocation.isSupported});
token.addResponder(new AsyncResponder(
function(event:ResultEvent, token:AsyncToken):void
{
// Map the location to city and state from the response address component
location = event.result.GeocodeResponse.result[0].address_component[3].long_name + ', '+ event.result.GeocodeResponse.result[0].address_component[5].long_name;
dispatchEvent(new Event("locationUpdate"));
},
function (event:FaultEvent, token:AsyncToken):void
{
// fail silently
trace("Reverse geocoding error: " + event.fault.faultString);
}));
}
}
}