Multiple taxonomies, what should the permalink look like?

Just released today a plugin named Cortex, freely available on GitHub. You need PHP 5.4+ and Composer to install it (docs).

This plugin allows to write custom ‘routes’ to queries, it is based on Symfony routing component so mechanism of adding routes is similar to that.

Here you’ll find full docs for Cortex.

Using Cortex all you need is a simple route:

add_action( 'brain_loaded', function() {

  Brain\Routes::add( '/ads/{cat}/{area}', 'cat_area_ad' )
  ->requirements( [ 'cat' => '[a-z]+', 'area' => 'area-[0-9]+' ] )
  ->query( function( $matches ) {
    return [
       'post_type' => 'ad',
       'tax_query' => [
          'relation' => 'AND',
          [
            'taxonomy' => 'category',
            'terms'    => [ $matches['cat'] ],
            'field'    => 'slug'
          ],
          [
            'taxonomy' => 'area',
            'terms'    => [ $matches['area'] ],
            'field'    => 'slug'
          ]
       ]
    ];
  } );

} );

"cat_area_ad" is an ID I assigned to route, that allows to easily build url for the route (see Cortex docs):

function get_cat_area_url( $cat="", $area="" ) {
  if ( did_action( 'parse_request' ) && ! empty( $cat ) && ! empty( $area ) ) {
    if ( strpos( $area, 'area-' ) !== 0 ) {
      $area="area-" . $area;
    }
    return Brain\Routes::url( 'cat_area_ad',  [ 'cat' => $cat, 'area' => $area ] );
  } else {
    return home_url();
  }
}

Previous function allows to get an url for the route, but both area and category must be passed as function arguments.

Once reading OP seems that “area” terms are named in the form "area-XX" (where ‘XX’ is a number) I made the route require that format and I also made get_cat_area_url function automatically prepend “area-” to the area string passed as argument (if not already present), so you can use it like so:

$url = get_cat_area_url( 'bikes', '5' );
echo $url; // will be: example.com/ads/bikes/area-5

get_cat_area_url function works properly only if 'parse_request' hook have been triggered (due to a limitation of Cortex url() API method), but in any template file you can use it without problems because when a template is shown that hook has always been triggered.