I love a good challenge!
The function here:
function get_post_categories_sorted ( $post_id ){
$terms = wp_get_post_terms( $post_id, 'category' );
$sorted_terms = [];
foreach( $terms as $term ){
$depth = count( get_ancestors( $term->term_id, 'category' ) );
if( ! array_key_exists( $depth, $sorted_terms ) ){
$sorted_terms[$depth] = [];
}
$sorted_terms[$depth][] = $term;
}
return $sorted_terms;
}
will give you an array with the structure in the following image. $sorted_terms[0]
contains the top level terms, $sorted_terms[1]
all the second level term and so on.
It would definitely be worth using a cache or the transients API to store the data as it could get expensive with a lot of terms to sort through as the call to get_ancestors involves a call back to the database for each level it has to traverse back up the chain.
EDIT
You can use this something like the following:
$sorted_terms = get_post_categories_sorted( get_the_ID() );
foreach( $sorted_terms as $key => $level_items ){
$number = $key + 1;
echo sprintf( '<p>Level %s Categories</p>', $number );
echo '<ul>';
foreach( $level_items as $term ){
echo sprintf( '<li>%s</li>', $term->name );
}
echo '</ul>';
}
EDIT 2
To just use a specific level, you can simplify the code above like this:
$sorted_terms = get_post_categories_sorted( get_the_ID() );
echo '<p>Level 1 Categories</p>';
echo '<ul>';
//Change the 0 here to whatever level you need,
//just remember is is 0 based; IE the first level is 0, 2nd level is 1 etc.
//If the number doesn't exist, PHP will throw an undefined index error.
foreach( $sorted_items[0] as $term ){
echo sprintf( '<li>%s</li>', $term->name );
}
echo '</ul>';