Чтобы реализовать механизм выделения, нам достаточно править только файл main.py.

Итоговый файл в конце этого текста, а тут уточнения что имено мы делаем, по кусочкам.

Добавить класс-плагин для селектора

class PolygonSelector(folium.MacroElement):
    _template = template.Template("""
        {% macro script(this, kwargs) %}
                                  
        let lastSelectedPolygon = null;
        {{this._parent.get_name()}}.on('draw:created', (e) => {
            let coordinates = e.layer.getLatLngs()[0].map(p => [p.lat, p.lng]);
            lastSelectedPolygon = e.layer;
            {{ this._parent.get_name()}}.addLayer(lastSelectedPolygon);
            
            if(mainWindow) {
                mainWindow.select_bounds(JSON.stringify(coordinates));
            }
        })
                                  
        {{this._parent.get_name()}}.on('draw:drawstart', (e) => {
            if (lastSelectedPolygon) {
                lastSelectedPolygon.remove();
                lastSelectedPolygon = null;                  
            }                
        })
                                       
        {% endmacro %}
""")

Преобразуем датасет в GeoDataFrame

внутри метода __init__ сразу после считывания датасета добавляем преобразование

class MainWindow(QMainWindow):
    def __init__(self):
        # ...
        self.df = pd.read_csv("./data.csv")
        
        # ПРЕОБРАЗУЕМ ДАТАСЕТ в GeoDataFrame
        self.df = gpd.GeoDataFrame(
            self.df,
            geometry=gpd.points_from_xy(self.df.latitude_dd, self.df.longitude_dd),
            crs='EPSG:4326'
        )
        # КОНЕЦ ПРЕОБРАЗОВАНИЯ
        
        # ...

Добавляем метод-слот обработчика выбора области

    # ОБРАБОТЧИК СОБЫТИЯ ВЫБОРА ОБЛАСТИ
    # мы тут по сути просто список городов выводим на форму
    @Slot(str)
    def select_bounds(self, coordinates_str):
        coordinates = json.loads(coordinates_str)
        
        self.polygon = Polygon(coordinates)
        cities = self.cities[self.cities.geometry.within(self.polygon)]
        print(cities.shape[0])
        cities_list = []
        for c in cities.itertuples():
            cities_list.append(f"{c.settlement}: {c.population}")

        self.ui.edtInfo.setText(f"""
Население: {cities.population.sum()}<p>
Города: {"<br>".join(cities_list)}
""")    
        # КОНЕЦ ОБРАБОТЧИКА

Подключение плагина-селектора к карте

def show_map(self):
    m = folium.Map(
        location=[52.286387, 104.280660],
        zoom_start=11,
        tiles="CartoDB Voyager",
        attributionControl=0,
        drawControl=True
    )
    m.add_child(BridgeFolium())
    
    # АУТИВИРУЕМ НАШ ПЛАГИН
    m.add_child(PolygonSelector())
    
    m.get_root().html.add_child(folium.Element("""<script src="qrc:///qtwebchannel/qwebchannel.js"></script>"""))
    
    # ПОДКЛЮЧЕНИЕ js и css БИБЛИОТЕК
    m.add_js_link('leaflet.draw.js', 'https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js')
    m.add_css_link('leaflet.draw.css', 'https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw-src.css')
    # КОНЕЦ ПОДКЛЮЧЕНИЯ
    
    self.channel = QWebChannel()
    self.channel.registerObject("mainWindow", self)
    self.ui.web.page().setWebChannel(self.channel)

    left, right = self.ui.cmbPopulation.currentData()
    
    # А ТУТ cities на self.cities заменяем
    self.cities = self.df.loc[(left <= self.df.population) & (self.df.population <=right)]

    sw = self.cities[['latitude_dd', 'longitude_dd']].min().values.tolist()
    ne = self.cities[['latitude_dd', 'longitude_dd']].max().values.tolist()
        
    
    for city in self.cities.itertuples():
        # ...
    
    # дальше ничего не трогаем ...

Итоговый код

Вот итоговый код main.py, который нужен чтобы заработала функция выделения:

import json
import sys
import os
import PySide6
from PySide6.QtWidgets import QApplication, QMainWindow
from PySide6.QtWebChannel import QWebChannel
from PySide6.QtCore import Slot
from shapely import Point, Polygon
from mainwindow import Ui_MainWindow
import folium
from folium import template
import io
import json
import pandas as pd
import geopandas as gpd
from shapely import Polygon

dirname = os.path.dirname(PySide6.__file__)
plugin_path = os.path.join(dirname, 'plugins', 'platforms')
os.environ['QT_QPA_PLATFORM_PLUGIN_PATH'] = plugin_path


class BridgeFolium(folium.MacroElement):
    _template = template.Template(
        """
        {% macro script(this, kwargs) %}
        var mainWindow;
        
        // Ждём загрузки страницы
        document.addEventListener('DOMContentLoaded', function() {
            // Создаём WebChannel
            new QWebChannel(qt.webChannelTransport, function(channel) {
                mainWindow = channel.objects.mainWindow;
            });
            
            // Ждём загрузки карты Leaflet
            setTimeout(function() {
                // Добавляем обработчик клика
                {{this._parent.get_name()}}.on('click', function(e) {
                    var lat = e.latlng.lat;
                    var lng = e.latlng.lng;
                    
                    if (mainWindow) {
                        mainWindow.receive_coordinates(lat, lng);
                    }
                });
            }, 1000);
        });
               
        {% endmacro %}
            """
    )


class ClickableMarker(folium.Marker):
    _template = template.Template(
        """
        {% macro script(this, kwargs) %}
            var {{ this.get_name() }} = L.marker(
                {{ this.location|tojson }},
                {{ this.options|tojavascript }}
            ).addTo({{ this._parent.get_name() }}).on('click', (e) => {
                var lat = e.latlng.lat;
                var lng = e.latlng.lng;
                
                if (mainWindow) {
                    mainWindow.marker_clicked(lat, lng, {{ this.options|tojavascript }}.id);
                }
            });
        {% endmacro %}
        """
    )

# СОЗДАЕМ ПЛАГИН ДЛЯ ДОБАВЛЕНИЯ ВОЗМОЖНОСТИ ВЫБРАТЬ ОБЛАСТЬ
class PolygonSelector(folium.MacroElement):
    _template = template.Template("""
        {% macro script(this, kwargs) %}
                                  
        let lastSelectedPolygon = null;
        {{this._parent.get_name()}}.on('draw:created', (e) => {
            let coordinates = e.layer.getLatLngs()[0].map(p => [p.lat, p.lng]);
            lastSelectedPolygon = e.layer;
            {{ this._parent.get_name()}}.addLayer(lastSelectedPolygon);
            
            if(mainWindow) {
                mainWindow.select_bounds(JSON.stringify(coordinates));
            }
        })
                                  
        {{this._parent.get_name()}}.on('draw:drawstart', (e) => {
            if (lastSelectedPolygon) {
                lastSelectedPolygon.remove();
                lastSelectedPolygon = null;                  
            }                
        })
                                       
        {% endmacro %}
""")
# КОНЕЦ ПЛАГИНА


class MainWindow(QMainWindow):
    def __init__(self):
        super(MainWindow, self).__init__()
        self.ui = Ui_MainWindow()
        self.ui.setupUi(self)

        self.polygon = None
        self.df = pd.read_csv("./data.csv")
        
        # ПРЕОБРАЗУЕМ ДАТАСЕТ в GeoDataFrame
        self.df = gpd.GeoDataFrame(
            self.df,
            geometry=gpd.points_from_xy(self.df.latitude_dd, self.df.longitude_dd),
            crs='EPSG:4326'
        )
        # КОНЕЦ ПРЕОБРАЗОВАНИЯ

        self.ui.cmbPopulation.addItem('50000-100000', (50001, 100000))
        self.ui.cmbPopulation.addItem('100000-500000', (100001, 500000))
        self.ui.cmbPopulation.addItem('500000-1млн.', (500001, 1000000))
        self.ui.cmbPopulation.addItem('от 1млн.', (1000001, 100000000))
        self.ui.cmbPopulation.currentIndexChanged.connect(self.show_map)

        self.show_map()

    @Slot(float, float)
    def receive_coordinates(self, lat, lng):
        print(lat, lng)

    
    # ОБРАБОТЧИК СОБЫТИЯ ВЫБОРА ОБЛАСТИ
    # мы тут по сути просто список городов выводим на форму
    @Slot(str)
    def select_bounds(self, coordinates_str):
        coordinates = json.loads(coordinates_str)
        
        self.polygon = Polygon(coordinates)
        cities = self.cities[self.cities.geometry.within(self.polygon)]
        print(cities.shape[0])
        cities_list = []
        for c in cities.itertuples():
            cities_list.append(f"{c.settlement}: {c.population}")

        self.ui.edtInfo.setText(f"""
Население: {cities.population.sum()}<p>
Города: {"<br>".join(cities_list)}
""")    
        # КОНЕЦ ОБРАБОТЧИКА


    @Slot(float, float, int)
    def marker_clicked(self, lat, lng, _id):
        city = self.df[self.df.id == _id].iloc[0]
        self.ui.edtCity.setText(city.settlement)
        self.ui.edtInfo.setText(f"""
Население: {city.population}<br>
Муниципалитет: {city.municipality}<br>
Регион: {city.region}
        """.strip())
        

    def show_map(self):
        m = folium.Map(
            location=[52.286387, 104.280660],
            zoom_start=11,
            tiles="CartoDB Voyager",
            attributionControl=0,
            drawControl=True
        )
        m.add_child(BridgeFolium())
        
        # АУТИВИРУЕМ НАШ ПЛАГИН
        m.add_child(PolygonSelector())

        m.get_root().html.add_child(folium.Element("""<script src="qrc:///qtwebchannel/qwebchannel.js"></script>"""))
        
        # ПОДКЛЮЧЕНИЕ js и css БИБЛИОТЕК
        m.add_js_link('leaflet.draw.js', 'https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js')
        m.add_css_link('leaflet.draw.css', 'https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw-src.css')
        # КОНЕЦ ПОДКЛЮЧЕНИЯ
        
        self.channel = QWebChannel()
        self.channel.registerObject("mainWindow", self)
        self.ui.web.page().setWebChannel(self.channel)


        left, right = self.ui.cmbPopulation.currentData()
        
        # А ТУТ cities на self.cities заменяем
        self.cities = self.df.loc[(left <= self.df.population) & (self.df.population <=right)]

        sw = self.cities[['latitude_dd', 'longitude_dd']].min().values.tolist()
        ne = self.cities[['latitude_dd', 'longitude_dd']].max().values.tolist()
        for city in self.cities.itertuples():
            color = 'blue'

            ClickableMarker(
                location=[city.latitude_dd, city.longitude_dd],
                id=city.id,
                icon=folium.Icon(icon="cat", prefix='fa', color=color)
            ).add_to(m)

        m.fit_bounds([sw, ne])

        # подключение карты к интерфейсу
        m_data = io.BytesIO()
        m.save(m_data, close_file=False)

        with open('result.html', 'w') as f:
            f.write(m_data.getvalue().decode())

        self.ui.web.setHtml(m_data.getvalue().decode())


if __name__ == "__main__":
    app = QApplication(sys.argv)

    window = MainWindow()
    window.show()

    sys.exit(app.exec())

Задание

Построение аналитики по выбранной области