A barometric pressure sensor measures the pressure from the column of
air above the sensor -- it does not measure altitude above the ground.
Fortunately, there is a (mostly) direct correlation. However, air
compresses and expands and the pressure exerted by the column is not
constant. The correlation between pressure and altitude needs to be
corrected for this local variation in air density. This correction is
of great interest to airplane pilots, so the government makes sure
every airport makes that information freely available.
Because the temperature and barometric pressure are randomly changing
all the time, so to be the most accurate you will need to know the
current standard pressure in your locale.
You can first fetch the current barometer value for the nearest
airport, then use that value instead of PRESSURE_STANDARD_ATMOSPHERE.
You will need to know the station ID, or specify a lat/long rectangle.
See METAR station specifications.
BUT... If your current error is only 2 meters, that is already
very good. You will never get an exact altitude from a barometric
altimeter -- that is why radar altimeters were invented.
http://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=xml&stationString=KSFO&hoursBeforeNow=1&mostRecent=true
This returns the most recent METAR for KSFO (San Francisco):
<?xml version="1.0" encoding="UTF-8"?>
<response xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XML-Schema-instance" version="1.2" xsi:noNamespaceSchemaLocation="http://aviationweather.gov/adds/schema/metar1_2.xsd">
<request_index>34600121</request_index>
<data_source name="metars" />
<request type="retrieve" />
<errors />
<warnings />
<time_taken_ms>1</time_taken_ms>
<data num_results="1">
<METAR>
<raw_text>KSFO 061756Z 30011KT 10SM FEW008 BKN160 19/13 A2992 RMK AO2 SLP132 T01890133 10189 20150 51006</raw_text>
<station_id>KSFO</station_id>
<observation_time>2015-08-06T17:56:00Z</observation_time>
<latitude>37.62</latitude>
<longitude>-122.37</longitude>
<temp_c>18.9</temp_c>
<dewpoint_c>13.3</dewpoint_c>
<wind_dir_degrees>300</wind_dir_degrees>
<wind_speed_kt>11</wind_speed_kt>
<visibility_statute_mi>10.0</visibility_statute_mi>
<altim_in_hg>29.920275</altim_in_hg>
<sea_level_pressure_mb>1013.2</sea_level_pressure_mb>
<quality_control_flags>
<auto_station>TRUE</auto_station>
</quality_control_flags>
<sky_condition sky_cover="FEW" cloud_base_ft_agl="800" />
<sky_condition sky_cover="BKN" cloud_base_ft_agl="16000" />
<flight_category>VFR</flight_category>
<three_hr_pressure_tendency_mb>0.6</three_hr_pressure_tendency_mb>
<maxT_c>18.9</maxT_c>
<minT_c>15.0</minT_c>
<metar_type>SPECI</metar_type>
<elevation_m>3.0</elevation_m>
</METAR>
</data>
</response>
You want to pull the value for sea_level_pressure_mb
which is 1013.2.
Here is a fragment that displays the current barometric pressure on a
map of your current location.
I started writing the "right" code to parse the XML, but lost interest
when I had to walk down the nodes. I left that as a learning exercise
and wrote the "hack" version instead.
/***********************************************************************/
/** Page2: Simple example of using Google Map. **/
/** TeasingDart **/
/***********************************************************************/
package com.nlited.twopages;
import android.app.Activity;
import android.location.Criteria;
import android.location.Location;
import android.location.LocationListener;
import android.location.LocationManager;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.google.android.gms.maps.CameraUpdateFactory;
import com.google.android.gms.maps.GoogleMap;
import com.google.android.gms.maps.MapFragment;
import com.google.android.gms.maps.MapView;
import com.google.android.gms.maps.model.LatLng;
import com.google.android.gms.maps.model.MarkerOptions;
import org.w3c.dom.Document;
import org.xml.sax.InputSource;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.StringReader;
import java.net.HttpURLConnection;
import java.net.URL;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
public class Page2 extends MapFragment implements LocationListener {
private static Page2 mObj;
private Activity mActivity;
private View mView;
private MapView mMapView;
private GoogleMap mMap;
private LocationManager mLocMgr;
private String mLocProvider;
private Location mLoc;
private LatLng mLatLng= new LatLng(43,-122);
private boolean mSlpFound= false;
private float mSLP= 1012;
private String mStationID;
private long mNextSlpCheck= 0;
static public Page2 getInstance() {
if(mObj==null)
mObj= new Page2();
return(mObj);
}
public Page2() {
Debug("constructor %s", this.toString());
mObj= this;
}
@Override public void onAttach(Activity activity) {
Debug("onAttach()");
mActivity= (Main)activity;
super.onAttach(activity);
startLocation();
new Thread(new GetSLP(),"GetSLP").start();
}
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle state) {
mActivity= (Main)getActivity();
Debug("onCreateView()");
super.onCreateView(inflater, container, state);
mView= inflater.inflate(R.layout.frag_page2, container, false);
mMapView= (MapView)mView.findViewById(R.id.map);
mMapView.onCreate(state);
mMapView.onResume();
mMap= mMapView.getMap();
mapUpdate();
return(mView);
}
/***********************************************************************/
/** Update the map using my GPS location and current SLP. **/
/***********************************************************************/
private void mapUpdate() {
MarkerOptions opt = new MarkerOptions();
if(mMap!=null) {
String status= mSlpFound ? String.format("%.1f %s",mSLP,mStationID) : "????";
mMap.clear();
mMap.moveCamera(CameraUpdateFactory.newLatLngZoom(mLatLng, 15));
mMap.addMarker(opt.position(mLatLng).title(status));
}
}
/***********************************************************************/
/** Retrieve my current GPS location. **/
/***********************************************************************/
private void startLocation() {
mLocMgr= (LocationManager)mActivity.getSystemService(Activity.LOCATION_SERVICE);
if(mLocMgr==null) {
Debug("Location services are not available.");
} else if(!mLocMgr.isProviderEnabled(LocationManager.GPS_PROVIDER)) {
Debug("GPS is not enabled.");
} else {
mLocProvider= mLocMgr.getBestProvider(new Criteria(),false);
if(mLocProvider==null) {
Debug("No GPS providers available.");
} else {
mLoc = mLocMgr.getLastKnownLocation(mLocProvider);
mLocMgr.requestLocationUpdates(mLocProvider,30000,1,this);
}
}
}
@Override public void onLocationChanged(Location loc) {
Debug("Updated location: %f %f",loc.getLatitude(),loc.getLongitude());
mLoc= loc;
mLatLng= new LatLng(mLoc.getLatitude(), mLoc.getLongitude());
if(System.currentTimeMillis() >= mNextSlpCheck)
new Thread(new GetSLP(),"GetSLP").start();
mapUpdate();
}
@Override public void onStatusChanged(String provider, int status, Bundle state) {
Debug("Location %s state is now %d.",provider,status);
if(status>0)
mapUpdate();
}
@Override public void onProviderEnabled(String provider) {
Debug("Location %s is now enabled.",provider);
}
@Override public void onProviderDisabled(String provider) {
Debug("Location %s is now disabled.",provider);
}
/***********************************************************************/
/** Background task to request the sea level pressure (SLP) from **/
/** the closest reporting station. **/
/***********************************************************************/
private class GetSLP implements Runnable {
public void run() {
int range;
boolean found= false;
//Next check in 15 minutes from now.
mNextSlpCheck= System.currentTimeMillis()+15*60*1000;
for(range=10;range<100;range+=10) {
URL url = buildUrl(range);
if(url!=null) {
String xml = fetch(url);
if(xml!=null) {
//found= parse(xml);
String slp= hack("sea_level_pressure_mb",xml);
if(slp!=null) {
mSLP= Float.parseFloat(slp);
mStationID= hack("station_id", xml);
found = true;
break;
}
}
}
}
if(found) {
Debug("Station found within %dkm.", range);
mSlpFound = true;
mActivity.runOnUiThread(new Runnable(){ public void run() { mapUpdate(); } });
} else {
Debug("No stations found within 100km!");
}
}
//Build the request URL using a radial range from the current (longitude,latitude)
private URL buildUrl(int range) {
URL url= null;
String protocol= "http";
String host= "www.aviationweather.gov";
String page= "adds/dataserver_current/httpparam";
String parms= "dataSource=metars&requestType=retrieve&format=xml&hoursBeforeNow=1&mostRecent=true";
parms+= String.format("&radialDistance=%d;%f,%f",range,mLatLng.longitude,mLatLng.latitude);
String urlStr= String.format("%s://%s/%s?%s",protocol,host,page,parms);
try {
url = new URL(urlStr);
} catch(Exception ex) {
Debug("buildUrl(%s) blew up.", urlStr);
}
return(url);
}
//Fetch the most current METARS report as an xml document.
private String fetch(URL url) {
String text= null;
try {
HttpURLConnection connect = (HttpURLConnection)url.openConnection();
InputStream strm= connect.getInputStream();
BufferedReader reader= new BufferedReader(new InputStreamReader(strm));
StringBuilder xml= new StringBuilder();
String line;
while((line= reader.readLine())!=null) {
xml.append(line);
}
reader.close();
text= xml.toString();
} catch(Exception ex) {
Debug("GetSLP.fetch() blew up.");
}
return(text);
}
//Quick hack version to extract a value from the xml string.
private String hack(String name, String xml) {
String value= null;
String tag= String.format("<%s>",name);
int nStart= xml.indexOf(tag);
if(nStart>=0) {
int nEnd = xml.indexOf("</", nStart+tag.length());
if(nEnd>nStart) {
value = xml.substring(nStart+tag.length(), nEnd);
}
}
return(value);
}
//The proper (but incomplete) method to parse the xml document.
private boolean parse(String xml) {
boolean found= false;
Float slp= 0.0f;
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
InputSource src= new InputSource();
src.setCharacterStream(new StringReader(xml));
Document doc = builder.parse(src);
//TODO: Walk the nodes down to "sea_level_pressure_mb"
//slp= Float.parseFloat(value);
//found= true;
} catch(Exception ex) {
Debug("GetSLP.parse() blew up.");
}
return(found);
}
}
private void Debug(String fmt, Object... args) { Log.w("2Page:Page2", String.format(fmt, args)); }
}
//EOF: PAGE2.JAVA
Layout:
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:paddingLeft="@dimen/activity_horizontal_margin"
android:paddingRight="@dimen/activity_horizontal_margin"
android:paddingTop="@dimen/activity_vertical_margin"
android:paddingBottom="@dimen/activity_vertical_margin"
android:background="@color/ForestGreen"
tools:context=".Page2"
>
<TextView
android:id="@+id/Page2_Title"
android:text="This is Page Two"
android:textSize="24pt"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:gravity="center"
/>
<com.google.android.gms.maps.MapView
android:id="@+id/map"
android:name="com.google.android.gms.maps.SupportMapFragment"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:layout_below="@+id/Page2_Title"
/>
</RelativeLayout>