<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Substance Labs &#187; PHP</title>
	<atom:link href="http://labs.findsubstance.com/category/php/feed/" rel="self" type="application/rss+xml" />
	<link>http://labs.findsubstance.com</link>
	<description>Create. Constantly.</description>
	<lastBuildDate>Wed, 27 Jul 2011 18:18:57 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.3.1</generator>
		<item>
		<title>Taxonomy Taxi</title>
		<link>http://labs.findsubstance.com/2011/07/27/taxonomy-taxi/</link>
		<comments>http://labs.findsubstance.com/2011/07/27/taxonomy-taxi/#comments</comments>
		<pubDate>Wed, 27 Jul 2011 18:18:57 +0000</pubDate>
		<dc:creator>Eric Eaglstun</dc:creator>
				<category><![CDATA[PHP]]></category>
		<category><![CDATA[Productivity]]></category>
		<category><![CDATA[Wordpress]]></category>

		<guid isPermaLink="false">http://labs.findsubstance.com/?p=433</guid>
		<description><![CDATA[Custom taxonomies are a great feature of WordPress that let you categorize your content beyond the default categories and tags. Setting them is as easy as finding the right plugin (I like Custom Post Type UI) or a little bit of digging into the code. However, once you start to get a fair amount of content on [...]]]></description>
			<content:encoded><![CDATA[<p><a href="http://labs.findsubstance.com/wp-content/uploads/2011/07/taxi.png"><img class="alignnone size-full wp-image-452" title="taxi" src="http://labs.findsubstance.com/wp-content/uploads/2011/07/taxi.png" alt="" width="500" height="289" /></a></p>
<p>Custom taxonomies are a great feature of WordPress that let you categorize your content beyond the default categories and tags. Setting them is as easy as finding the right plugin (I like <a href="http://wordpress.org/extend/plugins/custom-post-type-ui/">Custom Post Type UI</a>) or a little bit of <a href="http://codex.wordpress.org/Taxonomies#Custom_Taxonomies">digging into the code</a>.</p>
<p>However, once you start to get a fair amount of content on your site, you can quickly find the admin a little lacking if you&#8217;re using more than one set of categories and tags to classify your content. We&#8217;ve released a plugin that should fix this problem, and it&#8217;s over at <a href="http://wordpress.org/extend/plugins/taxonomy-taxi/">http://wordpress.org/extend/plugins/taxonomy-taxi/</a>.</p>
<p>If you&#8217;re already working with custom taxonomies on your site, simply install and activate the plugin. Head on back over to your main edit page (/wp-admin/edit.php) and you&#8217;ll see all of your taxonomies displayed in the main table.</p>
<p>I&#8217;ll run through a quick example. It&#8217;s nearly lunch time and I&#8217;ve got sausages on the brain. I want to categorize my posts by sausage type so have set up a <em>sausage</em> taxonomy.</p>
<p><img class="alignnone size-full wp-image-439" title="screen2" src="http://labs.findsubstance.com/wp-content/uploads/2011/07/screen2.png" alt="" width="800" height="444" /></p>
<p>I&#8217;ve added a few posts now, and you can see that the custom <em>sausage</em> taxonomy is showing in the main edit table. Since my theme supports post formats (which is technically another taxonomy) those show in the list as well.</p>
<p><img class="alignnone size-full wp-image-438" title="screen1" src="http://labs.findsubstance.com/wp-content/uploads/2011/07/screen1.png" alt="" width="800" height="444" /></p>
<p>In addition to being able to sort by any column, you&#8217;re also able to filter posts via the drop downs above.</p>
<p>Custom taxonomies have never been so delicious! Like every other piece of code, it&#8217;s sure to have bugs, so let me know if you find any. Hope this comes in handy for you, let me know if you have any suggestions &#8211; or, just leave me a nice rating on the download page.</p>
]]></content:encoded>
			<wfw:commentRss>http://labs.findsubstance.com/2011/07/27/taxonomy-taxi/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Working with AMFPHP and the Google Maps Flash API</title>
		<link>http://labs.findsubstance.com/2009/09/25/working-with-amfphp-and-the-google-maps-flash-api/</link>
		<comments>http://labs.findsubstance.com/2009/09/25/working-with-amfphp-and-the-google-maps-flash-api/#comments</comments>
		<pubDate>Fri, 25 Sep 2009 18:11:50 +0000</pubDate>
		<dc:creator>Shaun Tinney</dc:creator>
				<category><![CDATA[AS3]]></category>
		<category><![CDATA[Flash]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Google Maps]]></category>

		<guid isPermaLink="false">http://labs.findsubstance.com/?p=200</guid>
		<description><![CDATA[We&#8217;ve been working a lot with the Google Maps Flash API lately; recent projects Ride Oregon and Oregon Bounty make regular use of Google Maps to plot various types of data. Each project raised unique challenges, the solutions to which were rarely found in Google&#8217;s documentation. A note about setup: for data storage, retrieval, and [...]]]></description>
			<content:encoded><![CDATA[<p>We&#8217;ve been working a lot with the <a href="http://code.google.com/apis/maps/documentation/flash/">Google Maps Flash <abbr title="Application Programming Interface">API</abbr></a> lately; recent projects <a href="http://rideoregonride.com/">Ride Oregon</a> and <a href="http://bounty.traveloregon.com/">Oregon Bounty</a> make regular use of Google Maps to plot various types of data. Each project raised unique challenges, the solutions to which were rarely found in Google&#8217;s documentation.</p>
<p>A note about setup: for data storage, retrieval, and manipulation, we use <a href="http://www.amfphp.org/"><abbr title="Action Message Format PHP">AMFPHP</abbr></a>; for our local dev environment, <a href="http://mamp.info"><abbr title="Macintosh, Apache, Mysql and PHP">MAMP</abbr></a> and <a href="http://clickontyler.com/virtualhostx/">VirtualHostX</a>, managed with a <a href="http://www.smashingmagazine.com/2008/09/18/the-top-7-open-source-version-control-systems/">version control system</a>. That said, here are are some tips for working with the <a href="http://maps.googleapis.com/maps/flash/release/sdk.zip">Google Maps Flash <abbr title="Software Development Kit">SDK</abbr></a>:</p>
<ul>
<li><a href="#creating-custom-markers">Creating Custom Markers</a></li>
<li><a href="#tracking-markers">Tracking Markers Outside the Viewport</a></li>
<li><a href="#panning-markers">Panning Markers Back Into the Viewport</a></li>
<li><a href="#centering-on-markers">Centering on a Group of Markers</a></li>
<li><a href="#getting-elevation-data">Getting Elevation Data</a></li>
<li><a href="#plotting-long-routes">Plotting Long Routes</a></li>
<li><a href="#saving-an-image">Saving an Image of the Map</a></li>
<li><a href="#custom-amfphp-services">Custom AMFPHP Services</a></li>
</ul>
<p>
<object classid="clsid:D27CDB6E-AE6D-11cf-96B8-444553540000" width="100%" height="395"> <param name="movie" value="/d/working-with-amfphp-and-the-google-maps-flash-api/map.swf" /> <param name="flashvars" value="apiKey=ABQIAAAA5z4K5KRtblC3mI7YX8Jp5RR7vnDOwvFcfFFvuaGW1Qd73xkJahQi2mvFBFAKclaabioQvA3_i5peHA&amp;gateway=/amfphp/gateway.php" /> <!--[if !IE]>--> <object type="application/x-shockwave-flash" data="/d/working-with-amfphp-and-the-google-maps-flash-api/map.swf" width="100%" height="395"> <param name="flashvars" value="apiKey=ABQIAAAA5z4K5KRtblC3mI7YX8Jp5RR7vnDOwvFcfFFvuaGW1Qd73xkJahQi2mvFBFAKclaabioQvA3_i5peHA&amp;gateway=/amfphp/gateway.php" /> <!--<![endif]--> <a href="http://adobe.com/go/getflashplayer">You need the Adobe Flash player to view this.</a> <!--[if !IE]>--> </object> <!--<![endif]--> </object>
</p>
<h2 id="creating-custom-markers">Creating Custom Markers</h2>
<p>To easily create custom markers, extend the <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#Marker">Marker</a> class and pass a custom icon display into <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#MarkerOptions">MarkerOptions</a> from the constructor. This keeps everything contained in one object, and makes references to the custom icon a snap.</p>
<pre><code>// extends Marker;
public var icon : CustomIcon;
public function CustomMarker ( latLng : LatLng )
{
  icon = new CustomIcon();
  icon.latLng = latLng;

  super( latLng, new MarkerOptions( { icon : icon } ) );
}</code></pre>
<h2 id="tracking-markers">Tracking Markers Outside the Viewport</h2>
<p>Tracking markers when they go offstage can be a pain, fortunately there are some <a href="http://code.google.com/p/gmaps-utility-library-flash/">useful libraries</a> written to help with that kind of thing. I&#8217;ve also included our custom implementation (<a href="/d/working-with-amfphp-and-the-google-maps-flash-api/classes/GhostTracker.as">GhostTracker.as</a>) as a part of the <a href="/d/working-with-amfphp-and-the-google-maps-flash-api/working-with-amfphp-and-the-google-maps-flash-api.zip">source code</a> for this post.</p>
<h2 id="panning-markers">Panning Markers Back Into the Viewport</h2>
<p>When a marker is offstage and is selected by the user, the map can be centered or panned to move the marker detail into view. Centering the map with <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#Map.setCenter">setCenter</a> can be a jarring user experience, since opening any marker will pan the map view; an alternate approach is to use the <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#Map.panBy">panBy</a> function to accomplish the same thing with only the minimal necessary movement:</p>
<pre><code>var pt : Point = map.fromLatLngToViewport( latLng );
var np : Point = new Point();
var edgePadding : Number = 10;
var center : Number = _detail.width * 0.5 + edgePadding * 2;
var displayHeight : Number = _detail.height + edgePadding;
var mapRect : Rectangle = new Rectangle( 0, 0, map.width, map.height );

// if x is less than left boundary ( data sort window right edge );
if ( pt.x - center &lt; mapRect.x ) np.x = ( pt.x - center ) - mapRect.x; // if x is greater than right edge; if ( pt.x + center &gt; mapRect.right - edgePadding ) np.x = ( pt.x + center ) - ( mapRect.right - edgePadding );

// if y is greater than map height - padding;
if ( pt.y &gt; mapRect.bottom - edgePadding ) np.y = pt.y - ( mapRect.bottom - edgePadding );

// if y - height is less than top edge;
if ( pt.y - displayHeight &lt; edgePadding * 2 ) np.y = ( pt.y - displayHeight ) - edgePadding * 2;

// position map so that full display area is visible;
map.panBy( np );</code></pre>
<h2 id="centering-on-markers">Centering on a Group of Markers</h2>
<p>When working with an array of markers that need to be displayed together within the viewport, a <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#LatLngBounds">LatLngBounds</a> object can be extended to include all <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#LatLng">LatLng</a> points. From there, it&#8217;s just a matter of setting the center of the map to the center of the bounds area.</p>
<pre><code>var bounds : LatLngBounds = new LatLngBounds();
var marker : Marker;
var latLng : LatLng;
var maxZoom : Number = 13;
var t : int = markers.length;
for ( var i : int = 0; i &lt; t; i++ )
{
  marker = markers[ i ] as Marker;

  latLng = marker.getLatLng();

  if ( latLng.lat() != 0 &amp;&amp; latLng.lng() != 0 ) bounds.extend( latLng );
}

if ( !bounds.isEmpty() ) map.setCenter( bounds.getCenter(), Math.min( maxZoom, map.getBoundsZoomLevel( bounds ) ) );</code></pre>
<h2 id="getting-elevation-data">Getting Elevation Data</h2>
<p>Elevation data for routes and markers can be retrieved and stored efficiently via PHP, or at runtime in AS3, using a couple of <a href="http://www.geonames.org/export/web-services.html">different</a> <a href="http://gisdata.usgs.gov/XMLWebServices/TNM_Elevation_Service.php">sources</a>.</p>
<pre><code>// PHP with vars $lat &amp; $lng (returns value in meters);
$primary = "http://ws.geonames.org/srtm3?lat=$lat&amp;lng=$lng";
$secondary = "http://gisdata.usgs.gov/xmlwebservices2/elevation_service.asmx/getElevation?X_Value=$lng&amp;Y_Value=$lat&amp;Elevation_Units=METERS&amp;Source_Layer=-1&amp;Elevation_Only=TRUE";</code></pre>
<h2 id="plotting-long-routes">Plotting Long Routes</h2>
<p>When plotting routes on custom maps in Ride Oregon, we often had to exceed the 25 point max in the <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#Directions.loadFromWaypoints">loadFromWaypoints</a> function of the <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#Directions">Directions</a> class. The solution was simply to queue multiple requests, with the last point from the previous request as the first point in the new one.</p>
<pre><code>// assumes class variable _waypoints is an array containing stored points to get directions for;
var max : Number = 25;
var tot : Number = _waypoints.length;
var req : Number = Math.ceil( _waypoints.length / max )
var per : Number = Math.min( max, tot );
var first : Marker;

// split directions into multiple requests;
for ( var j : int = 0; j &lt; req; j++ )
{
  // store latLng points;
  var waypoints : Array = [];

  // store first recorded point as first point in new query;
  if ( first ) waypoints.push( first.getLatLng() );

  per = Math.min( max, tot );

  // create series of directions with max or fewer waypoints;
  for ( var k : int = 0; k &lt; per; k++ )
  {
    var w : Marker = _waypoints[ k + j * max ];

    waypoints.push( w.getLatLng() );
  }

  // reduce total;
  tot -= max;

  // store first point;
  first = w;

  // break if invalid request;
  if ( waypoints.length &lt;= 1 ) break;

  // set up new directions instance;
  var directions : Directions = new Directions( new DirectionsOptions( { avoidHighways : true } ) );
  directions.addEventListener( DirectionsEvent.DIRECTIONS_SUCCESS, onDirectionsSuccess );
  directions.addEventListener( DirectionsEvent.DIRECTIONS_FAILURE, onDirectionsFailure );

  // get new set of directions;
  directions.loadFromWaypoints( waypoints );
}</code></pre>
<p>Listen for a response <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#DirectionsEvent">DirectionsEvent</a> to be dispatched:</p>
<pre><code>function onDirectionsSuccess ( e : DirectionsEvent ) : void
{
  // push e.directions to array of direction requests;
  // add to e.directions.distance to total route distance;
  // add e.directions.createPolyline to map;
}

function onDirectionsFailure ( e : DirectionsEvent ) : void
{
  // failed;
}</code></pre>
<h2 id="saving-an-image">Saving an Image of the Map</h2>
<p>In order to create no-flash versions of all the maps, we combined the display layer of the map, with Google&#8217;s <a href="http://code.google.com/apis/maps/documentation/staticmaps/">Static Map API</a>. Initially, we went this route because there was no other option (Google had banned access to their images via BitmapData.draw, and it would throw a SecurityError), but they&#8217;ve since <a href="http://code.google.com/p/gmaps-api-issues/issues/detail?id=545#c26">removed the restriction</a> and added a <a href="http://code.google.com/apis/maps/documentation/flash/reference.html#Map.getPrintableBitmap">getPrintableBitmap</a> function to the API. Still, if you load content into your icons from another domain, or just don&#8217;t want to deal with crossdomain policy files, layering the Static API is a decent solution.</p>
<pre><code>// resolves Error #2121: Security sandbox violation: BitmapData.draw
function saveMapImage () : void
{
  var rect : Rectangle = new Rectangle( 0, 0, Math.min( map.width, 640 ), Math.min( map.height, 640 ) );
  var bmd : BitmapData = new BitmapData( rect.width, rect.height, true, 0x00000000 );
  var mapDisplay : Sprite = Sprite( Sprite( Sprite( map.getDisplayObject() ).getChildAt( 1 ) ).getChildAt( 2 ) );

  // draw custom overlay;
  bmd.draw( mapDisplay, null, null, null, rect, true );

  // encode ByteArray and send to server...
}</code></pre>
<h2 id="custom-amfphp-services">Custom AMFPHP Services</h2>
<p>We&#8217;ve put together an abstract set of <a href="/d/working-with-amfphp-and-the-google-maps-flash-api/classes/amfphp/AMFService.as">AMFService</a> and <a href="/d/working-with-amfphp-and-the-google-maps-flash-api/classes/amfphp/AMFServiceEvent.as">AMFServiceEvent</a> classes to work with AMFPHP. This way you can write a service in PHP, and call its methods directly on an instance of the AMFService class in ActionScript. There are plenty of features that could be added to this implementation (service method and parameter checking, for instance), but for most cases its a clean and flexible approach. If you have a method called getCustomData defined in CustomService.php, setting up an interface is simple:</p>
<pre><code>var customService : AMFService = new AMFService( 'CustomService', '/amfphp/gateway.php' );
customService.addEventListener( AMFServiceEvent.RETURN, onServiceData );
customService.getCustomData();

function onServiceData ( e : AMFServiceEvent ) : void
{
  trace( e.data );
}</code></pre>
<p>Hopefully a few of these techniques save some headaches when working with Google Maps in Flash. Feel free to post a comment with your own solutions.</p>
]]></content:encoded>
			<wfw:commentRss>http://labs.findsubstance.com/2009/09/25/working-with-amfphp-and-the-google-maps-flash-api/feed/</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

