Getting my head round WordPress filter

You are using the filter wrongly, that is why you might not get the desired result. Lets first look at the template_include filter which you can check out in wp-includes/template-loader.php (current version 4.3.1)

44  if ( defined('WP_USE_THEMES') && WP_USE_THEMES ) :
45          $template = false;
46          if     ( is_404()            && $template = get_404_template()            ) :
47          elseif ( is_search()         && $template = get_search_template()         ) :
48          elseif ( is_front_page()     && $template = get_front_page_template()     ) :
49          elseif ( is_home()           && $template = get_home_template()           ) :
50          elseif ( is_post_type_archive() && $template = get_post_type_archive_template() ) :
51          elseif ( is_tax()            && $template = get_taxonomy_template()       ) :
52          elseif ( is_attachment()     && $template = get_attachment_template()     ) :
53                  remove_filter('the_content', 'prepend_attachment');
54          elseif ( is_single()         && $template = get_single_template()         ) :
55          elseif ( is_page()           && $template = get_page_template()           ) :
56          elseif ( is_singular()       && $template = get_singular_template()       ) :
57          elseif ( is_category()       && $template = get_category_template()       ) :
58          elseif ( is_tag()            && $template = get_tag_template()            ) :
59          elseif ( is_author()         && $template = get_author_template()         ) :
60          elseif ( is_date()           && $template = get_date_template()           ) :
61          elseif ( is_archive()        && $template = get_archive_template()        ) :
62          elseif ( is_comments_popup() && $template = get_comments_popup_template() ) :
63          elseif ( is_paged()          && $template = get_paged_template()          ) :
64          else :
65                  $template = get_index_template();
66          endif;
67          /**
68           * Filter the path of the current template before including it.
69           *
70           * @since 3.0.0
71           *
72           * @param string $template The path of the template to include.
73           */
74          if ( $template = apply_filters( 'template_include', $template ) )
75                  include( $template );
76          return;
77  endif;

Because we are on a real page, the value of $template that is passed to the filter in line 74 will be the value of get_page_template() (because is_page() returned true) which value is filterable through the {$type}_template filter located in get_query_template() (which is used by get_page_template()).

OK, so we can tackle this two ways, either use template_include or page_template (remember the {$type}_template filter, here $type === 'page'). If you are going to use template_include, you will need to use the is_page() condition to target real pages only. If you use the page_template filter (which is more desired here), you do not need this check.

I’m not really sure what the following line is suppose to do, and most probably why your filter never reach beyond this section

if (!isset( $this->templates[get_post_meta($post->ID, '_wp_page_template', true ) ] ) ) {
    return $template;
}

Because you are dealing with true pages, _wp_page_template should always have a value, so all we really need to do is to check if our template exists inside the template and then returning it

You can try the following:

public function view_project_template( $template ) {

    // Use get_queried_object_id() to get page id, much more reliable
    $current_page_id = get_queried_object_id();

    $page_template = get_post_meta( 
        $current_page_id, // Current page ID
        '_wp_page_template', // Meta key holding the page template value
        true // Return single value
    );

    /**
     * You can add extra protection and make sure $page_template has a value
     * although it should not be really necessary
     */
    // if ( !$page_template )
        // return $template;

    $file = plugin_dir_path(__FILE__) . $page_template;

    // Check if file exists, if not, return $template
    if ( !file_exists( $file ) ) 
        return $template;

    // If we reached this part, we can include our new template
    return $template = $file; // Or you can simply do return $file;

}

You should then hook your method as follow (as this is inside a class)

add_filter( 'page_template', [$this, 'view_project_template'] );

Just a small thought here, why not make the method private as you would not want it available outside of the class