Basic Button query - Click button, Zoom to GPS location

4766
7
07-12-2012 10:33 PM
JohnAllen
New Contributor III
I have my app setup currently so when I hit "map it" button on my home page a new activity starts displaying ESRI basemap and my GPS location.

I want to do a simple modification, so that when I click the "map it" button on my home page ESRI basemap appears only.

Then have a button that I can select inside the map view that will zoom to my GPS location.

Basically instead of a one part process have a two part process.

This is simple but I'm not sure how to code it.

Thanks,
0 Kudos
7 Replies
JohnAllen
New Contributor III
Ok here is what I got so far....
The code below is for the .java in source folder.
See the red text portion of the code below.
Can someone give me advice as to what to put to make the button work?

Note: I put TEST for my map service server, so if you copy and paste it want show.

package com.dhec.MPOS;

import com.esri.android.map.LocationService;
import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISDynamicMapServiceLayer;
import com.esri.android.map.ags.ArcGISTiledMapServiceLayer;
import com.esri.android.map.event.OnStatusChangedListener;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.LinearUnit;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.geometry.Unit;

import android.app.Activity;
import android.content.Intent;
import android.location.Location;
import android.location.LocationListener;
import android.media.MediaPlayer;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class Mapscreen extends Activity {
 
 MapView map = null;
  
     // The circle area specified by search_radius and input lat/lon serves
  // searching purpose. It is also used to construct the extent which
  // map zooms to after the first GPS fix is retrieved.
  final static double SEARCH_RADIUS = 5;

  protected static final Intent OnStatusChangedListener = null;

 /** Called when the activity is first created. */
 public void onCreate(Bundle savedInstanceState) {
  super.onCreate(savedInstanceState);
  setContentView(R.layout.mapscreen);

  // Retrieve the map and initial extent from XML layout
  map = (MapView)findViewById(R.id.map);
    
  // Add dynamic layer to MapView
  map.addLayer(new ArcGISTiledMapServiceLayer("" +
  "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"));
  map.addLayer(new ArcGISDynamicMapServiceLayer("" +
     "http://TEST/ArcGIS/rest/services/Mobile/MPOS/MapServer"));
  
  //Button Sound
        final MediaPlayer buttonSound = MediaPlayer.create(Mapscreen.this, R.raw.watersplash);
  
  //Setting up the button references
        Button ZoomToMyLocation = (Button) findViewById (R.id.button3);        
        
       //When clicked, Activates the Zoom To GPS process
        ZoomToMyLocation.setOnClickListener(new View.OnClickListener() {
   
         public void onClick(View v) {
       // TODO Auto-generated method stub
       buttonSound.start();
       
                       //I need the ZoomToMyLocation button to excute here I think?

       map.setOnStatusChangedListener(new OnStatusChangedListener(){

                        //Starts searching for the GPS location of the phone. Below here I've gotten this code to work before, but not
                        //with my custom button above. 
   private static final long serialVersionUID = 1L;

   public void onStatusChanged(Object source, STATUS status) {
    if (source == map && status == STATUS.INITIALIZED) {
     LocationService ls = map.getLocationService();
     ls.setAutoPan(false);
     ls.setLocationListener(new LocationListener() {

      boolean locationChanged = false;

      // Zooms to the current location when first GPS fix
      // arrives.
      public void onLocationChanged(Location loc) {
       if (!locationChanged) {
        locationChanged = true;
        double locy = loc.getLatitude();
        double locx = loc.getLongitude();
        Point wgspoint = new Point(locx, locy);
        Point mapPoint = (Point) GeometryEngine
          .project(wgspoint,
            SpatialReference.create(4326),
            map.getSpatialReference());

        Unit mapUnit = map.getSpatialReference()
          .getUnit();
        double zoomWidth = Unit.convertUnits(
          SEARCH_RADIUS,
          Unit.create(LinearUnit.Code.MILE_US),
          mapUnit);
        Envelope zoomExtent = new Envelope(mapPoint,
          zoomWidth, zoomWidth);
        map.setExtent(zoomExtent);

       }

      }

      public void onProviderDisabled(String arg0) {

      }

      public void onProviderEnabled(String arg0) {
      }

      public void onStatusChanged(String arg0, int arg1,
        Bundle arg2) {

      }
     });
     ls.start();

    }

   }
  });
    
    }});

 }

 protected void onPause() {
  super.onPause();
  map.pause();
 }

 protected void onResume() {
  super.onResume();
  map.unpause();
 }
}
0 Kudos
deleted-user-ATjHIWsdQYmT
Occasional Contributor III
Don't Instantiate the onStatusChangedListener() inside the click event of your button.  Move it outside of the buttonclick event. 
Also, you could set a class-level variable for the status, capture it when you call the onStatusChangedListener, then use it's return in your "if" statement on the buttonclick. 

I think the way you have it set up now your pattern is as follows:
Click button-->check for status changed-->if initialized then locate.
since the status of the map has not changed, your not calling your LocationService.
0 Kudos
JohnAllen
New Contributor III
Ok, I figured out what I needed.

The code below allows me to open ESRI basemap streets.
Once inside the street view I have one button called "Zoom to My Location"
If I click the button it will track/zoom to my location.
If I click the button again it will stop tracking my location.
Pretty basic, but what I needed.

@AndrewB - Thanks for the "if" statement advice. It got me thinking about work flow for this.

After doing some reading I found that I didn't need this: map.setOnStatusChangedListener(new OnStatusChangedListener()



This is what I my new code looks like:

package com.dhec.MPOS;

import com.esri.android.map.LocationService;
import com.esri.android.map.MapView;
import com.esri.android.map.ags.ArcGISDynamicMapServiceLayer;
import com.esri.android.map.ags.ArcGISTiledMapServiceLayer;
import com.esri.core.geometry.Envelope;
import com.esri.core.geometry.GeometryEngine;
import com.esri.core.geometry.LinearUnit;
import com.esri.core.geometry.Point;
import com.esri.core.geometry.SpatialReference;
import com.esri.core.geometry.Unit;

import android.app.Activity;
import android.location.Location;
import android.location.LocationListener;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class Mapscreen extends Activity {
 
  // The circle area specified by search_radius and input lat/lon serves
  // searching purpose. It is also used to construct the extent which
  // map zooms to after the first GPS fix is retrieved.
  final static double SEARCH_RADIUS = 5;
 
  MapView map = null;
  Button ZoomToMyLocation;

    /** Called when the activity is first created. */
 public void onCreate(Bundle savedInstanceState) {
      super.onCreate(savedInstanceState);
   setContentView(R.layout.mapscreen);

    // Retrieve the map and initial extent from XML layout
    map = (MapView) findViewById (R.id.map);
      
    // This adds ESRI's titled map service layer "World Street Map" to the MapView, as well as my 
    // dynamics map service "MPOS"
    map.addLayer(new ArcGISTiledMapServiceLayer("" +
    "http://services.arcgisonline.com/ArcGIS/rest/services/World_Street_Map/MapServer"));
    map.addLayer(new ArcGISDynamicMapServiceLayer("" +
    "http://TEST/ArcGIS/rest/services/Mobile/MPOS/MapServer"));
  
//Setting up the button reference
          ZoomToMyLocation = (Button) findViewById (R.id.button1);
              
          //When "Button ZoomToMyLocation" is clicked, method "public void onClick(View v)" sets up for location listening. 
          ZoomToMyLocation.setOnClickListener(new View.OnClickListener() {
     
            public void onClick(View v) {
          LocationService ls = map.getLocationService();
     ls = map.getLocationService();
     ls.setAccuracyCircleOn(true);
     ls.setAutoPan(true);
       ls.setLocationListener(new LocationListener() {

      boolean locationChanged = false;

        // Zooms to the current location when first GPS fix arrives.
     public void onLocationChanged(Location loc) {
      if (!locationChanged) {
       locationChanged = true;
       double locy = loc.getLatitude();
       double locx = loc.getLongitude();
       Point wgspoint = new Point(locx, locy);
       Point mapPoint = (Point) GeometryEngine
         .project(wgspoint,
           SpatialReference.create(4326),
           map.getSpatialReference());

       Unit mapUnit = map.getSpatialReference()
         .getUnit();
       double zoomWidth = Unit.convertUnits(
         SEARCH_RADIUS,
         Unit.create(LinearUnit.Code.MILE_US),
         mapUnit);
       Envelope zoomExtent = new Envelope(mapPoint,
         zoomWidth, zoomWidth);
       map.setExtent(zoomExtent);

      }

     }

     public void onProviderDisabled(String arg0) {

     }

     public void onProviderEnabled(String arg0) {
     }

     public void onStatusChanged(String arg0, int arg1,
       Bundle arg2) {

     }
    });    
     
           if (ls.isStarted() == false){
          ls.start();
           }
     
           else {
          ls.stop();
           };      
   
         }
          
          });
    } 
}
0 Kudos
deleted-user-ATjHIWsdQYmT
Occasional Contributor III
It's true that the onStatusChanged(); listener isn't required, but it is a good idea to implement it when using GPS.  If your map is not initialized and you try to enable the GPS, you will most likely encounter some sort of error. You could listen to onStatusChanged and if the state is INITIALIZATION_FAILED  or LAYER_LOADING_FAILED  then disable the button.  Again, not required, but good programming practice.

Good luck.
0 Kudos
AndyGup
Esri Regular Contributor
I agree with Andrew. Other examples are when you are running an application with multiple views (Activities), or when you minimize the application and then start it back up. During those conditions and depending on how you coded your app the GPS may resume before the map is loaded and then your mapping code may throw an error since the map won't be ready.

When you minimize the app or return to your MapView Activity, the best practice is to test to make sure the map is loaded after onResume() is called. You can accomplish this by using a Handler. Each Handler runs a single thread off of the main User Interface and you can check to see if the STATUS.INITIALIZED has occured. And once the map is loaded (and visible) then use the GPS. Using this pattern you can ensure that everything happens in the correct order and you can fail gracefully if, for example, that map didn't load because of a poor internet connection. If anyone needs the code for this scenario ping me agup at esri dot com.

I also blogged about using onResume() here: http://www.andygup.net/?p=779.

-Andy
0 Kudos
JohnAllen
New Contributor III
Andrew & Andy,

Thanks for the suggestions.
I think I understand what your talking about, and it makes sense.
My problem is applying it to the code I already have.

Can ya'll post some short code for how you've used any of the following?
  1. setOnStatusChangedListener(new OnStatusChangedListener()
  2. INITIALIZATION_FAILED
  3. LAYER_LOADING_FAILED
  4. STATUS.INITIALIZED

I've used some of the 'Nearby' sample code already, but would like to see some additional code relating to the above.

Thanks for your help.

John
0 Kudos
AndyGup
Esri Regular Contributor
John, here's a code snippet that should help. It doesn't cover everything you asked for but should give you a good idea of how to implement the listeners.

@Override
public void onStatusChanged(Object source, STATUS status) {
  if (OnStatusChangedListener.STATUS.INITIALIZED == status && source == _mapView) {
   _isMapLoaded = true;
  setMapType(_maptype);
  
  
  if(_center_lat == null || _center_lon == null){
   latlon = new Point(_DEFAULT_LON,_DEFAULT_LAT);
  }
  else{
   latlon = new Point(_center_lon,_center_lat);
  }
  Point point = (Point)GeometryEngine.project(latlon,SpatialReference.create(4326), _mapView.getSpatialReference());  
  _mapView.centerAt(point,false);
  
  if(_center_scale == null){
   _mapView.setScale(_DEFAULT_SCALE);
  }
  else{
   _mapView.setScale(_center_scale);
  }     
   
  Log.d("MapViewController", "setOnStatusChangedListener() STATUS: " + status);  
  dispatchStatusEvent(new MapViewControllerEvent(MapViewEvent.STATUS_INITIALIZED), status.toString()); //custom event            
   
  }
  if (OnStatusChangedListener.STATUS.LAYER_LOADING_FAILED == status && source == _mapView){
   Log.d("MapViewController", "setMapListeners() STATUS: " + status + ", " + status.toString());
   displayToast("There was a map problem with loading a layer: " + status.toString());
   dispatchStatusErrorEvent(new MapViewControllerEvent(MapViewEvent.STATUS_LAYER_LOADING_FAILED), status.toString()); //custom event
  }
}

-Andy
0 Kudos