public static function get_search_area_categories() {
        if (!isset(static::$searchareacategories)) {
            $categories = self::get_core_search_area_categories();

            // Go through all existing search areas and get categories they are assigned to.
            $areacategories = [];
            foreach (self::get_search_areas_list() as $searcharea) {
                foreach ($searcharea->get_category_names() as $categoryname) {
                    if (!key_exists($categoryname, $areacategories)) {
                        $areacategories[$categoryname] = [];
                    }

                    $areacategories[$categoryname][] = $searcharea;
                }
            }

            // Populate core categories by areas.
            foreach ($areacategories as $name => $searchareas) {
                if (key_exists($name, $categories)) {
                    $categories[$name]->set_areas($searchareas);
                } else {
                    throw new \coding_exception('Unknown core search area category ' . $name);
                }
            }

            // Get additional categories.
            $additionalcategories = self::get_additional_search_area_categories();
            foreach ($additionalcategories as $additionalcategory) {
                if (!key_exists($additionalcategory->get_name(), $categories)) {
                    $categories[$additionalcategory->get_name()] = $additionalcategory;
                }
            }

            // Remove categories without areas.
            foreach ($categories as $key => $category) {
                if (empty($category->get_areas())) {
                    unset($categories[$key]);
                }
            }

            // Sort categories by order.
            uasort($categories, function($category1, $category2) {
                return $category1->get_order() > $category2->get_order();
            });

            static::$searchareacategories = $categories;
        }

        return static::$searchareacategories;
    }