Exemple #1
0
    def make_gui_mouse(self, parent):
        """Build the mouse part of the controls part of GUI.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ')
        self.mouse_position = ROTextCtrl(parent,
                                         '',
                                         size=(150, -1),
                                         tooltip=('Shows the mouse '
                                                  'longitude and latitude '
                                                  'on the map'))

        # lay out the controls
        sb = AppStaticBox(parent, 'Mouse position')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt,
                border=PackBorder,
                flag=(wx.ALIGN_CENTER_VERTICAL
                      | wx.ALIGN_RIGHT | wx.LEFT))
        box.Add(self.mouse_position,
                proportion=1,
                border=PackBorder,
                flag=wx.RIGHT | wx.TOP | wx.BOTTOM)

        return box
Exemple #2
0
    def make_gui_level(self, parent):
        """Build the control that shows the level.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ')
        self.map_level = ROTextCtrl(parent, '', size=(30,-1),
                                    tooltip='Shows map zoom level')

        # lay out the controls
        sb = AppStaticBox(parent, 'Map level')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt, flag=(wx.ALIGN_CENTER_VERTICAL |wx.LEFT))
        box.Add(self.map_level, proportion=0,
                flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)

        return box
Exemple #3
0
class AppFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self, None, size=DefaultAppSize, title=DemoName)
        self.SetMinSize(DefaultAppSize)
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panel.SetBackgroundColour(wx.WHITE)
        self.panel.ClearBackground()

        self.tile_source = tiles.Tiles()
        self.tile_directory = self.tile_source.tiles_dir

        # the data objects for map and view layers
        self.map_layer = None
        self.view_layer = None

        # build the GUI
        self.make_gui(self.panel)

        # finally, set up application window position
        self.Centre()

        # bind events to handlers
        self.pyslip.Bind(pyslip.EVT_PYSLIP_POSITION,
                         self.handle_position_event)
        self.pyslip.Bind(pyslip.EVT_PYSLIP_LEVEL, self.handle_level_change)

        # finally, goto desired level and position
        wx.CallLater(25, self.final_setup, InitViewLevel, InitViewPosition)

    def final_setup(self, level, position):
        """Perform final setup.

        level     zoom level required
        position  position to be in centre of view

        We do this in a CallAfter() function for those operations that
        must not be done while the GUI is "fluid".
        """

        self.pyslip.GotoLevelAndPosition(level, position)

#####
# Build the GUI
#####

    def make_gui(self, parent):
        """Create application GUI."""

        # start application layout
        all_display = wx.BoxSizer(wx.HORIZONTAL)
        parent.SetSizer(all_display)

        # put map view in left of horizontal box
        self.pyslip = pyslip.pySlip(parent,
                                    tile_src=self.tile_source,
                                    style=wx.SIMPLE_BORDER)
        all_display.Add(self.pyslip, proportion=1, border=1, flag=wx.EXPAND)

        # small spacer here - separate view and controls
        all_display.AddSpacer(HSpacerSize)

        # add controls to right of spacer
        controls = self.make_gui_controls(parent)
        all_display.Add(controls, proportion=0, border=1)

        parent.SetSizerAndFit(all_display)

    def make_gui_view(self, parent):
        """Build the map view widget

        parent  reference to the widget parent

        Returns the static box sizer.
        """

        # create gui objects
        sb = AppStaticBox(parent, '')
        #        tile_object = tiles.Tiles(self.tile_directory)
        self.pyslip = pyslip.pySlip(parent, tile_src=self.tile_source)

        # lay out objects
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(self.pyslip, proportion=1, border=1, flag=wx.EXPAND)

        return box

    def make_gui_controls(self, parent):
        """Build the 'controls' part of the GUI

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # all controls in vertical box sizer
        controls = wx.BoxSizer(wx.VERTICAL)

        # add the map level in use widget
        level = self.make_gui_level(parent)
        controls.Add(level, proportion=0, flag=wx.EXPAND | wx.ALL)

        # vertical spacer
        controls.AddSpacer(VSpacerSize)

        # add the mouse position feedback stuff
        mouse = self.make_gui_mouse(parent)
        controls.Add(mouse, proportion=0, flag=wx.EXPAND | wx.ALL)

        # buttons for each point of interest
        self.buttons = {}
        for (num, city) in enumerate(Cities):
            controls.AddSpacer(VSpacerSize)
            (lonlat, name) = city
            btn = wx.Button(parent, num, name)
            controls.Add(btn, proportion=0, flag=wx.EXPAND | wx.ALL)
            btn.Bind(wx.EVT_BUTTON, self.handle_button)
            self.buttons[num] = city
        return controls

    def make_gui_level(self, parent):
        """Build the control that shows the level.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ')
        self.map_level = wx.StaticText(parent, wx.ID_ANY, ' ')

        # lay out the controls
        sb = AppStaticBox(parent, 'Map level')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt,
                border=PackBorder,
                flag=(wx.ALIGN_CENTER_VERTICAL
                      | wx.ALIGN_RIGHT | wx.LEFT))
        box.Add(self.map_level,
                proportion=0,
                border=PackBorder,
                flag=wx.RIGHT | wx.TOP)

        return box

    def make_gui_mouse(self, parent):
        """Build the mouse part of the controls part of GUI.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ')
        self.mouse_position = ROTextCtrl(parent,
                                         '',
                                         size=(150, -1),
                                         tooltip=('Shows the mouse '
                                                  'longitude and latitude '
                                                  'on the map'))

        # lay out the controls
        sb = AppStaticBox(parent, 'Mouse position')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt,
                border=PackBorder,
                flag=(wx.ALIGN_CENTER_VERTICAL
                      | wx.ALIGN_RIGHT | wx.LEFT))
        box.Add(self.mouse_position,
                proportion=1,
                border=PackBorder,
                flag=wx.RIGHT | wx.TOP | wx.BOTTOM)

        return box

    ######
    # Exception handlers
    ######

    def handle_button(self, event):
        """Handle button event."""

        (posn, name) = self.buttons[event.Id]
        self.pyslip.GotoPosition(posn)

        if self.map_layer:
            self.pyslip.DeleteLayer(self.map_layer)
        map_data = [posn]
        point_colour = '#0000ff40'
        self.map_layer = self.pyslip.AddPointLayer(map_data,
                                                   map_rel=True,
                                                   placement='cc',
                                                   color=point_colour,
                                                   radius=11,
                                                   visible=True,
                                                   name='map_layer')

        if self.view_layer:
            self.pyslip.DeleteLayer(self.view_layer)
        view_data = [(
            ((0, 0), (0, -10), (0, 0), (0, 10), (0, 0), (-10, 0), (0, 0), (10,
                                                                           0)),
            {
                'colour': '#ff0000ff'
            },
        )]
        #        poly_colour = '#ff0000ff'
        self.view_layer = self.pyslip.AddPolygonLayer(
            view_data,
            map_rel=False,
            placement='cc',
            #                                                      colour=poly_colour,
            closed=False,
            visible=True,
            name='view_layer')

    def handle_position_event(self, event):
        """Handle a pySlip POSITION event."""

        posn_str = ''
        if event.mposn:
            (lon, lat) = event.mposn
            posn_str = ('%.*f / %.*f' %
                        (LonLatPrecision, lon, LonLatPrecision, lat))

        self.mouse_position.SetValue(posn_str)

    def handle_level_change(self, event):
        """Handle a pySlip LEVEL event."""

        self.map_level.SetLabel('%d' % event.level)
class AppFrame(wx.Frame):
    def __init__(self):
        wx.Frame.__init__(self,
                          None,
                          size=DefaultAppSize,
                          title='%s, test version %s' %
                          (DemoName, DemoVersion))
        self.SetMinSize(DefaultAppSize)
        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panel.SetBackgroundColour(wx.WHITE)
        self.panel.ClearBackground()

        self.tile_source = Tiles.Tiles()

        # build the GUI
        self.make_gui(self.panel)

        # set initial view position
        self.map_level.SetLabel('%d' % InitialViewLevel)
        wx.CallAfter(self.final_setup, InitialViewLevel, InitialViewPosition)

        # force pyslip initialisation
        self.pyslip.OnSize()

        # finally, set up application window position
        self.Centre()

        # initialise state variables
        self.text_layer = None
        self.text_view_layer = None

        # finally, bind pySlip events to handlers
        self.pyslip.Bind(pyslip.EVT_PYSLIP_POSITION,
                         self.handle_position_event)
        self.pyslip.Bind(pyslip.EVT_PYSLIP_LEVEL, self.handle_level_change)

#####
# Build the GUI
#####

    def make_gui(self, parent):
        """Create application GUI."""

        # start application layout
        all_display = wx.BoxSizer(wx.HORIZONTAL)
        parent.SetSizer(all_display)

        # put map view in left of horizontal box
        self.pyslip = pyslip.pySlip(parent,
                                    tile_src=self.tile_source,
                                    style=wx.SIMPLE_BORDER)
        all_display.Add(self.pyslip, proportion=1, border=0, flag=wx.EXPAND)

        # small spacer here - separate view and controls
        all_display.AddSpacer(HSpacerSize)

        # add controls to right of spacer
        controls = self.make_gui_controls(parent)
        all_display.Add(controls, proportion=0, border=0)

        parent.SetSizerAndFit(all_display)

    def make_gui_view(self, parent):
        """Build the map view widget

        parent  reference to the widget parent

        Returns the static box sizer.
        """

        # create gui objects
        sb = AppStaticBox(parent, '')
        self.pyslip = pyslip.pySlip(parent, tile_src=self.tile_source)

        # lay out objects
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(self.pyslip, proportion=1, border=0, flag=wx.EXPAND)

        return box

    def make_gui_controls(self, parent):
        """Build the 'controls' part of the GUI

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # all controls in vertical box sizer
        controls = wx.BoxSizer(wx.VERTICAL)

        # add the map level in use widget
        level = self.make_gui_level(parent)
        controls.Add(level, proportion=0, flag=wx.EXPAND | wx.ALL)

        # vertical spacer
        controls.AddSpacer(VSpacerSize)

        # add the mouse position feedback stuff
        mouse = self.make_gui_mouse(parent)
        controls.Add(mouse, proportion=0, flag=wx.EXPAND | wx.ALL)

        # vertical spacer
        controls.AddSpacer(VSpacerSize)

        # controls for map-relative text layer
        self.text = self.make_gui_text(parent)
        controls.Add(self.text, proportion=0, flag=wx.EXPAND | wx.ALL)
        self.text.Bind(EVT_DELETE, self.textDelete)
        self.text.Bind(EVT_UPDATE, self.textUpdate)

        # vertical spacer
        controls.AddSpacer(VSpacerSize)

        # controls for view-relative text layer
        self.text_view = self.make_gui_text_view(parent)
        controls.Add(self.text_view, proportion=0, flag=wx.EXPAND | wx.ALL)
        self.text_view.Bind(EVT_DELETE, self.textViewDelete)
        self.text_view.Bind(EVT_UPDATE, self.textViewUpdate)

        # vertical spacer
        controls.AddSpacer(VSpacerSize)

        return controls

    def make_gui_level(self, parent):
        """Build the control that shows the level.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ')
        self.map_level = wx.StaticText(parent, wx.ID_ANY, ' ')

        # lay out the controls
        sb = AppStaticBox(parent, 'Map level')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt,
                border=PackBorder,
                flag=(wx.ALIGN_CENTER_VERTICAL
                      | wx.ALIGN_RIGHT | wx.LEFT))
        box.Add(self.map_level,
                proportion=0,
                border=PackBorder,
                flag=wx.RIGHT | wx.TOP)

        return box

    def make_gui_mouse(self, parent):
        """Build the mouse part of the controls part of GUI.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ')
        self.mouse_position = ROTextCtrl(parent,
                                         '',
                                         size=(150, -1),
                                         tooltip=('Shows the mouse '
                                                  'longitude and latitude '
                                                  'on the map'))

        # lay out the controls
        sb = AppStaticBox(parent, 'Mouse position')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt,
                border=PackBorder,
                flag=(wx.ALIGN_CENTER_VERTICAL
                      | wx.ALIGN_RIGHT | wx.LEFT))
        box.Add(self.mouse_position,
                proportion=1,
                border=PackBorder,
                flag=wx.RIGHT | wx.TOP | wx.BOTTOM)

        return box

    def make_gui_text(self, parent):
        """Build the text part of the controls part of GUI.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create widgets
        text_obj = LayerControl(parent,
                                'Text, map-relative',
                                text=DefaultText,
                                font=DefaultFont,
                                fontsize=DefaultFontSize,
                                textcolour=DefaultTextColour,
                                pointradius=DefaultPointRadius,
                                pointcolour=DefaultPointColour,
                                placement=DefaultPlacement,
                                x=DefaultX,
                                y=DefaultY,
                                offset_x=DefaultOffsetX,
                                offset_y=DefaultOffsetY)

        return text_obj

    def make_gui_text_view(self, parent):
        """Build the view-relative text part of the controls part of GUI.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create widgets
        text_obj = LayerControl(parent,
                                'Text, view-relative',
                                text=DefaultViewText,
                                font=DefaultFont,
                                fontsize=DefaultFontSize,
                                textcolour=DefaultTextColour,
                                pointradius=DefaultPointRadius,
                                pointcolour=DefaultPointColour,
                                placement=DefaultViewPlacement,
                                x=DefaultViewX,
                                y=DefaultViewY,
                                offset_x=DefaultViewOffsetX,
                                offset_y=DefaultViewOffsetY)

        return text_obj

    ######
    # event handlers
    ######

##### map-relative text layer

    def textUpdate(self, event):
        """Display updated text."""

        if self.text_layer:
            self.pyslip.DeleteLayer(self.text_layer)

        # convert values to sanity for layer attributes
        text = event.text
        font = event.font
        fontsize = event.fontsize
        textcolour = event.textcolour
        pointradius = event.pointradius
        pointcolour = event.pointcolour

        placement = event.placement
        if placement == 'none':
            placement = ''

        x = event.x
        if not x:
            x = 0
        try:
            x = float(x)
        except ValueError:
            x = 0.0

        y = event.y
        if not y:
            y = 0
        try:
            y = float(y)
        except ValueError:
            y = 0.0

        off_x = event.offset_x
        if not off_x:
            off_x = 0
        try:
            off_x = int(off_x)
        except ValueError:
            x_off = 0

        off_y = event.offset_y
        if not off_y:
            off_y = 0
        try:
            off_y = int(off_y)
        except ValueError:
            off_y = 0

        text_data = [(x, y, text, {
            'placement': placement,
            'radius': pointradius,
            'fontname': font,
            'fontsize': fontsize,
            'colour': pointcolour,
            'textcolour': textcolour,
            'offset_x': off_x,
            'offset_y': off_y
        })]
        self.text_layer = self.pyslip.AddTextLayer(text_data,
                                                   map_rel=True,
                                                   visible=True,
                                                   name='<text_layer>')

    def textDelete(self, event):
        """Delete the text map-relative layer."""

        if self.text_layer:
            self.pyslip.DeleteLayer(self.text_layer)
        self.text_layer = None

##### view-relative text layer

    def textViewUpdate(self, event):
        """Display updated text."""

        if self.text_view_layer:
            self.pyslip.DeleteLayer(self.text_view_layer)

        # convert values to sanity for layer attributes
        text = event.text
        font = event.font
        fontsize = event.fontsize
        textcolour = event.textcolour
        pointradius = event.pointradius
        pointcolour = event.pointcolour

        placement = event.placement
        if placement == 'none':
            placement = ''

        x = event.x
        if not x:
            x = 0
        x = int(x)

        y = event.y
        if not y:
            y = 0
        y = int(y)

        off_x = event.offset_x
        if not off_x:
            off_x = 0
        off_x = int(off_x)

        off_y = event.offset_y
        if not off_y:
            off_y = 0
        off_y = int(off_y)

        # create a new text layer
        text_data = [(x, y, text, {
            'placement': placement,
            'radius': pointradius,
            'fontname': font,
            'fontsize': fontsize,
            'colour': pointcolour,
            'textcolour': textcolour,
            'offset_x': off_x,
            'offset_y': off_y
        })]
        self.text_view_layer = self.pyslip.AddTextLayer(text_data,
                                                        map_rel=False,
                                                        visible=True,
                                                        name='<text_layer>')

    def textViewDelete(self, event):
        """Delete the text view-relative layer."""

        if self.text_view_layer:
            self.pyslip.DeleteLayer(self.text_view_layer)
        self.text_view_layer = None

    def final_setup(self, level, position):
        """Perform final setup.

        level     zoom level required
        position  position to be in centre of view

        We do this in a CallAfter() function for those operations that
        must not be done while the GUI is "fluid".
        """

        self.pyslip.GotoLevelAndPosition(level, position)

    ######
    # Exception handlers
    ######

    def handle_position_event(self, event):
        """Handle a pySlip POSITION event."""

        posn_str = ''
        if event.mposn:
            (lon, lat) = event.mposn
            posn_str = ('%.*f / %.*f' %
                        (LonLatPrecision, lon, LonLatPrecision, lat))

        self.mouse_position.SetValue(posn_str)

    def handle_level_change(self, event):
        """Handle a pySlip LEVEL event."""

        self.map_level.SetLabel('%d' % event.level)
    def __init__(self,
                 parent,
                 title,
                 filename='',
                 placement=DefaultPlacement,
                 pointradius=DefaultPointRadius,
                 pointcolour=DefaultPointColour,
                 x=0,
                 y=0,
                 offset_x=0,
                 offset_y=0,
                 **kwargs):
        """Initialise a LayerControl instance.

        parent       reference to parent object
        title        text to show in static box outline
        filename     filename of image to show
        placement    placement string for image
        pointradius  radius of the image point
        pointcolour  colour of the image point
        x, y         X and Y coords
        offset_x     X offset of image
        offset_y     Y offset of image
        **kwargs     keyword args for Panel
        """

        # create and initialise the base panel
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, **kwargs)
        self.SetBackgroundColour(wx.WHITE)

        self.v_filename = filename
        self.v_placement = placement
        self.v_pointradius = str(pointradius)
        self.v_pointcolour = pointcolour
        self.v_x = x
        self.v_y = y
        self.v_offset_x = offset_x
        self.v_offset_y = offset_y

        box = AppStaticBox(self, title)
        sbs = wx.StaticBoxSizer(box, orient=wx.VERTICAL)
        gbs = wx.GridBagSizer(vgap=10, hgap=10)

        # row 0
        row = 0
        gbs.Add(3, 1, (row, 0))
        label = wx.StaticText(self, wx.ID_ANY, 'filename: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.filename = ROTextCtrl(self, self.v_filename)
        gbs.Add(self.filename, (row, 2), span=(1, 3), border=0, flag=wx.EXPAND)
        gbs.Add(3, 1, (row, 5))

        # row 1
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'placement: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        choices = [
            'nw', 'cn', 'ne', 'ce', 'se', 'cs', 'sw', 'cw', 'cc', 'none'
        ]
        style = wx.CB_DROPDOWN | wx.CB_READONLY
        self.placement = wx.ComboBox(self,
                                     value=self.v_placement,
                                     choices=choices,
                                     style=style)
        gbs.Add(self.placement, (row, 2), border=0)

        # row 2
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'point radius: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        style = wx.CB_DROPDOWN | wx.CB_READONLY
        self.pointradius = wx.ComboBox(self,
                                       value=self.v_pointradius,
                                       choices=PointRadiusChoices,
                                       style=style)
        gbs.Add(self.pointradius, (row, 2),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.EXPAND))
        label = wx.StaticText(self, wx.ID_ANY, 'point colour: ')
        gbs.Add(label, (row, 3),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.pointcolour = wx.Button(self, label='')
        self.pointcolour.SetBackgroundColour(self.v_pointcolour)
        gbs.Add(self.pointcolour, (row, 4), border=0, flag=wx.EXPAND)

        # row 3
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'x: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.x = wx.TextCtrl(self, value=str(self.v_x))
        gbs.Add(self.x, (row, 2), border=0, flag=wx.EXPAND)

        label = wx.StaticText(self, wx.ID_ANY, 'y: ')
        gbs.Add(label, (row, 3),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.y = wx.TextCtrl(self, value=str(self.v_y))
        gbs.Add(self.y, (row, 4), border=0, flag=wx.EXPAND)

        # row 4
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'offset_x: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.offset_x = wx.TextCtrl(self, value=str(self.v_offset_x))
        gbs.Add(self.offset_x, (row, 2), border=0, flag=wx.EXPAND)

        label = wx.StaticText(self, wx.ID_ANY, '  offset_y: ')
        gbs.Add(label, (row, 3),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.offset_y = wx.TextCtrl(self, value=str(self.v_offset_y))
        gbs.Add(self.offset_y, (row, 4), border=0, flag=wx.EXPAND)

        # row 5
        row += 1
        delete_button = wx.Button(self, label='Remove')
        gbs.Add(delete_button, (row, 2), border=10, flag=wx.EXPAND)
        update_button = wx.Button(self, label='Update')
        gbs.Add(update_button, (row, 4), border=10, flag=wx.EXPAND)

        sbs.Add(gbs)
        self.SetSizer(sbs)
        sbs.Fit(self)

        # row 6 (just a spacer)
        row += 1
        gbs.Add(1, 3, (row, 0))

        # bind events to handlers
        self.filename.Bind(wx.EVT_LEFT_UP, self.onFilename)
        self.pointcolour.Bind(wx.EVT_BUTTON, self.onPointColour)
        delete_button.Bind(wx.EVT_BUTTON, self.onDelete)
        update_button.Bind(wx.EVT_BUTTON, self.onUpdate)
class LayerControl(wx.Panel):
    def __init__(self,
                 parent,
                 title,
                 filename='',
                 placement=DefaultPlacement,
                 pointradius=DefaultPointRadius,
                 pointcolour=DefaultPointColour,
                 x=0,
                 y=0,
                 offset_x=0,
                 offset_y=0,
                 **kwargs):
        """Initialise a LayerControl instance.

        parent       reference to parent object
        title        text to show in static box outline
        filename     filename of image to show
        placement    placement string for image
        pointradius  radius of the image point
        pointcolour  colour of the image point
        x, y         X and Y coords
        offset_x     X offset of image
        offset_y     Y offset of image
        **kwargs     keyword args for Panel
        """

        # create and initialise the base panel
        wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, **kwargs)
        self.SetBackgroundColour(wx.WHITE)

        self.v_filename = filename
        self.v_placement = placement
        self.v_pointradius = str(pointradius)
        self.v_pointcolour = pointcolour
        self.v_x = x
        self.v_y = y
        self.v_offset_x = offset_x
        self.v_offset_y = offset_y

        box = AppStaticBox(self, title)
        sbs = wx.StaticBoxSizer(box, orient=wx.VERTICAL)
        gbs = wx.GridBagSizer(vgap=10, hgap=10)

        # row 0
        row = 0
        gbs.Add(3, 1, (row, 0))
        label = wx.StaticText(self, wx.ID_ANY, 'filename: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.filename = ROTextCtrl(self, self.v_filename)
        gbs.Add(self.filename, (row, 2), span=(1, 3), border=0, flag=wx.EXPAND)
        gbs.Add(3, 1, (row, 5))

        # row 1
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'placement: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        choices = [
            'nw', 'cn', 'ne', 'ce', 'se', 'cs', 'sw', 'cw', 'cc', 'none'
        ]
        style = wx.CB_DROPDOWN | wx.CB_READONLY
        self.placement = wx.ComboBox(self,
                                     value=self.v_placement,
                                     choices=choices,
                                     style=style)
        gbs.Add(self.placement, (row, 2), border=0)

        # row 2
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'point radius: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        style = wx.CB_DROPDOWN | wx.CB_READONLY
        self.pointradius = wx.ComboBox(self,
                                       value=self.v_pointradius,
                                       choices=PointRadiusChoices,
                                       style=style)
        gbs.Add(self.pointradius, (row, 2),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.EXPAND))
        label = wx.StaticText(self, wx.ID_ANY, 'point colour: ')
        gbs.Add(label, (row, 3),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.pointcolour = wx.Button(self, label='')
        self.pointcolour.SetBackgroundColour(self.v_pointcolour)
        gbs.Add(self.pointcolour, (row, 4), border=0, flag=wx.EXPAND)

        # row 3
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'x: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.x = wx.TextCtrl(self, value=str(self.v_x))
        gbs.Add(self.x, (row, 2), border=0, flag=wx.EXPAND)

        label = wx.StaticText(self, wx.ID_ANY, 'y: ')
        gbs.Add(label, (row, 3),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.y = wx.TextCtrl(self, value=str(self.v_y))
        gbs.Add(self.y, (row, 4), border=0, flag=wx.EXPAND)

        # row 4
        row += 1
        label = wx.StaticText(self, wx.ID_ANY, 'offset_x: ')
        gbs.Add(label, (row, 1),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.offset_x = wx.TextCtrl(self, value=str(self.v_offset_x))
        gbs.Add(self.offset_x, (row, 2), border=0, flag=wx.EXPAND)

        label = wx.StaticText(self, wx.ID_ANY, '  offset_y: ')
        gbs.Add(label, (row, 3),
                border=0,
                flag=(wx.ALIGN_CENTER_VERTICAL | wx.ALIGN_RIGHT))
        self.offset_y = wx.TextCtrl(self, value=str(self.v_offset_y))
        gbs.Add(self.offset_y, (row, 4), border=0, flag=wx.EXPAND)

        # row 5
        row += 1
        delete_button = wx.Button(self, label='Remove')
        gbs.Add(delete_button, (row, 2), border=10, flag=wx.EXPAND)
        update_button = wx.Button(self, label='Update')
        gbs.Add(update_button, (row, 4), border=10, flag=wx.EXPAND)

        sbs.Add(gbs)
        self.SetSizer(sbs)
        sbs.Fit(self)

        # row 6 (just a spacer)
        row += 1
        gbs.Add(1, 3, (row, 0))

        # bind events to handlers
        self.filename.Bind(wx.EVT_LEFT_UP, self.onFilename)
        self.pointcolour.Bind(wx.EVT_BUTTON, self.onPointColour)
        delete_button.Bind(wx.EVT_BUTTON, self.onDelete)
        update_button.Bind(wx.EVT_BUTTON, self.onUpdate)

    def onFilename(self, event):
        """Change image filename."""

        wildcard = ("PNG files (*.png)|*.png|"
                    "JPG files (*.jpg)|*.jpg|"
                    "All files (*.*)|*.*")

        filepath = None

        dialog = wx.FileDialog(None, "Choose an image file", os.getcwd(), "",
                               wildcard, wx.FD_OPEN)
        if dialog.ShowModal() == wx.ID_OK:
            filepath = dialog.GetPath()
        dialog.Destroy()

        if filepath:
            self.filename.SetValue(filepath)

    def onPointColour(self, event):
        """Change text colour."""

        colour = self.pointcolour.GetBackgroundColour()
        wxcolour = wx.ColourData()
        wxcolour.SetColour(colour)

        dialog = wx.ColourDialog(self, data=wxcolour)
        dialog.GetColourData().SetChooseFull(True)
        new_colour = None
        if dialog.ShowModal() == wx.ID_OK:
            new_colour = dialog.GetColourData().Colour
        dialog.Destroy()

        if new_colour:
            self.pointcolour.SetBackgroundColour(new_colour)

    def onDelete(self, event):
        """Remove image from map."""

        event = LayerControlEvent(myEVT_DELETE, self.GetId())
        self.GetEventHandler().ProcessEvent(event)

    def get_numeric_value(self, control):
        """Get numeric value of a textbox.

        control  the textbox to query

        Return numeric value as a float.
        """

        orig_value = control.GetValue()
        if not orig_value:
            value = '0'
            control.SetValue('0')
        else:
            value = orig_value

        try:
            value = float(value)
        except ValueError:
            return (orig_value, None)
        return (orig_value, value)

    def onUpdate(self, event):
        """Update image on map."""

        # get x/y/offset_x/offset_y and check valid
        (orig_x, x) = self.get_numeric_value(self.x)
        (orig_y, y) = self.get_numeric_value(self.y)
        (orig_offset_x, offset_x) = self.get_numeric_value(self.offset_x)
        (orig_offset_y, offset_y) = self.get_numeric_value(self.offset_y)

        if (x is not None and y is not None and offset_x is not None
                and offset_y is not None):
            event = LayerControlEvent(myEVT_UPDATE, self.GetId())

            event.filename = self.filename.GetValue()
            event.placement = self.placement.GetValue()
            event.radius = int(self.pointradius.GetValue())
            event.colour = self.pointcolour.GetBackgroundColour()
            event.x = self.x.GetValue()
            event.y = self.y.GetValue()
            event.offset_x = self.offset_x.GetValue()
            event.offset_y = self.offset_y.GetValue()

            self.GetEventHandler().ProcessEvent(event)
        else:
            msg = 'These controls have bad values:\n'
            if x is None:
                name = ('x:' + ' ' * 20)[:12]
                msg += "\t%s\t%s\n" % (name, str(orig_x))
            if y is None:
                name = ('y:' + ' ' * 20)[:12]
                msg += "\t%s\t%s\n" % (name, str(orig_y))
            if offset_x is None:
                name = ('offset_x:' + ' ' * 20)[:12]
                msg += "\t%s\t%s\n" % (name, str(orig_offset_x))
            if offset_y is None:
                name = ('offset_y:' + ' ' * 20)[:12]
                msg += "\t%s\t%s\n" % (name, str(orig_offset_y))
            wx.MessageBox(msg, 'Warning', wx.OK | wx.ICON_ERROR)
Exemple #7
0
class AppFrame(wx.Frame):
    def __init__(self):
        super().__init__(None, title='JC Geotag Tool')

        self.panel = wx.Panel(self, wx.ID_ANY)
        self.panel.SetBackgroundColour(wx.WHITE)
        self.panel.ClearBackground()
        all_display = wx.BoxSizer(wx.HORIZONTAL)
        self.panel.SetSizer(all_display)

        obj = __import__('pyslip', globals(), locals(), ['stamen_transport'])
        tileset = getattr(obj, 'stamen_transport')
        tile_obj = tileset.Tiles()

        # put map view in left of horizontal box
        self.pyslip = pyslip.pySlip(self.panel, tile_src=tile_obj,
                                    style=wx.SIMPLE_BORDER)
        all_display.Add(self.pyslip, proportion=1, flag=wx.EXPAND)

        # add controls at right
        controls = self.make_gui_controls(self.panel)
        all_display.Add(controls, proportion=0)

        self.panel.SetSizerAndFit(all_display)

        self.pyslip.Bind(pyslip.EVT_PYSLIP_LEVEL, self.level_change_event)
        self.pyslip.Bind(pyslip.EVT_PYSLIP_POSITION, self.mouse_posn_event)
        self.pyslip.Bind(wx.EVT_RIGHT_DOWN, self.right_click_event)

        original_img = '550D_2020-08-28_IMG_7103.jpg'
        thumb_img = '550D_2020-08-28_IMG_7103.jpg.thumb'
        img = Image.open(original_img)
        img.thumbnail((64, 64), Image.ANTIALIAS)
        img.save('550D_2020-08-28_IMG_7103.jpg.thumb', "JPEG")
        ShipImg = thumb_img
        ImageData = [
                     (120.0, 24.0, ShipImg, {'placement': 'cc'}),
                     # Venus - 1826
                     (120.1, 24.1, ShipImg, {'placement': 'ne'}),
                     # Wolverine - 1879
                     (120.2, 24.2, ShipImg, {'placement': 'nw'}),
                     # Thomas Day - 1884
                     (120.3, 24.3, ShipImg, {'placement': 'sw'}),
                     # Sybil - 1902
                     (120.4, 24.4, ShipImg, {'placement': 'se'}),
                     # Prince of Denmark - 1863
                     (120.5, 24.5, ShipImg),
                     # Moltke - 1911
                     (120.6, 24.6, ShipImg)
                    ]
        MRShowLevels = [3, 4, 5, 6, 7, 8, 9, 10]
        self.image_layer = \
                self.pyslip.AddImageLayer(ImageData, map_rel=True,
                                          visible=True,
                                          delta=10,
                                          show_levels=MRShowLevels,
                                          name='<image_layer>')
        self.point_layer = None

        self.pyslip.GotoLevelAndPosition(8, (118.01, 26.01))
        self.last_clicked_loc = None

        self.Centre()
        self.Show()
        self.Maximize(True)

    def make_point_layer(self, long, lat):
        PointData = [(long, lat)]
        MRShowLevels = [3, 4, 5, 6, 7, 8, 9, 10]
        self.point_layer = \
                self.pyslip.AddPointLayer(PointData, map_rel=True,
                                          colour='#ff0000f0', radius=6,
                                          # offset points to exercise placement
                                          offset_x=0, offset_y=0, visible=True,
                                          show_levels=MRShowLevels,
                                          delta=40,
                                          placement='nw',   # check placement
                                          name='<pt_layer>')
    
    def make_gui_controls(self, parent):
        """Build the 'controls' part of the GUI

        parent  reference to parent

        Returns reference to containing sizer object.

        Should really use GridBagSizer layout.
        """

        # all controls in vertical box sizer
        controls = wx.BoxSizer(wx.VERTICAL)

        # put level and position into one 'controls' position
        tmp = wx.BoxSizer(wx.HORIZONTAL)
        tmp.AddSpacer(HorizSpacer)
        level = self.make_gui_level(parent)
        tmp.Add(level, proportion=0, flag=wx.EXPAND|wx.ALL)
        tmp.AddSpacer(HorizSpacer)
        mouse = self.make_gui_mouse(parent)
        tmp.Add(mouse, proportion=0, flag=wx.EXPAND|wx.ALL)
        tmp.AddSpacer(HorizSpacer)

        controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL)
        controls.AddSpacer(VertSpacer)

        tmp = wx.BoxSizer(wx.HORIZONTAL)
        tmp.AddSpacer(HorizSpacer)
        self.file_list = wx.ListCtrl(parent, wx.ID_ANY, style = wx.LC_REPORT)
        self.file_list.InsertColumn(0, 'Path')
        tmp.Add(self.file_list, proportion=1, flag=wx.EXPAND|wx.ALL)
        tmp.AddSpacer(HorizSpacer)
        controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL)
        controls.AddSpacer(VertSpacer)

        tmp = wx.BoxSizer(wx.HORIZONTAL)
        tmp.AddSpacer(HorizSpacer)
        open_file_btn = wx.Button(parent, wx.ID_ANY, 'Open')
        tmp.Add(open_file_btn, proportion=1, flag=wx.EXPAND|wx.ALL)
        tmp.AddSpacer(HorizSpacer)
        controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL)
        controls.AddSpacer(VertSpacer)

        tmp = wx.BoxSizer(wx.HORIZONTAL)
        tmp.AddSpacer(HorizSpacer)
        set_location_btn = wx.Button(parent, wx.ID_ANY, 'Set Location')
        tmp.Add(set_location_btn, proportion=1, flag=wx.EXPAND|wx.ALL)
        tmp.AddSpacer(HorizSpacer)
        controls.Add(tmp, proportion=0, flag=wx.EXPAND|wx.ALL)
        controls.AddSpacer(VertSpacer)

        open_file_btn.Bind(wx.EVT_BUTTON, self.open_file_clicked)
        set_location_btn.Bind(wx.EVT_BUTTON, self.set_location_clicked)

        return controls

    def make_gui_level(self, parent):
        """Build the control that shows the level.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Level: ')
        self.map_level = ROTextCtrl(parent, '', size=(30,-1),
                                    tooltip='Shows map zoom level')

        # lay out the controls
        sb = AppStaticBox(parent, 'Map level')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt, flag=(wx.ALIGN_CENTER_VERTICAL |wx.LEFT))
        box.Add(self.map_level, proportion=0,
                flag=wx.LEFT|wx.ALIGN_CENTER_VERTICAL)

        return box

    def make_gui_mouse(self, parent):
        """Build the mouse part of the controls part of GUI.

        parent  reference to parent

        Returns reference to containing sizer object.
        """

        # create objects
        txt = wx.StaticText(parent, wx.ID_ANY, 'Lon/Lat: ')
        self.mouse_position = ROTextCtrl(parent, '', size=(120,-1),
                                         tooltip=('Shows the mouse '
                                                  'longitude and latitude '
                                                  'on the map'))

        # lay out the controls
        sb = AppStaticBox(parent, 'Mouse position')
        box = wx.StaticBoxSizer(sb, orient=wx.HORIZONTAL)
        box.Add(txt, flag=(wx.ALIGN_CENTER_VERTICAL |wx.LEFT))
        box.Add(self.mouse_position, proportion=0,
                flag=wx.RIGHT|wx.TOP|wx.BOTTOM)

        return box

    def mouse_posn_event(self, event):
        """Handle a "mouse position" event from the pySlipQt widget.
       
        The 'event' object has these attributes:
            event.etype  the type of event
            event.mposn  the new mouse position on the map (xgeo, ygeo)
            event.vposn  the new mouse position on the view (x, y)
        """

        if event.mposn:
            (lon, lat) = event.mposn
            # we clamp the lon/lat to zero here since we don't want small
            # negative values displaying as "-0.00"
            if abs(lon) < 0.01:
                lon = 0.0
            if abs(lat) < 0.01:
                lat = 0.0
            self.mouse_position.SetValue('%.2f/%.2f' % (lon, lat))
        else:
            self.mouse_position.SetValue('')
            
    def level_change_event(self, event):
        self.map_level.SetValue(str(event.level))

    def right_click_event(self, event):
        self.last_clicked_loc = self.pyslip.View2Geo(event.GetPosition())
        if self.point_layer != None:
            self.pyslip.DeleteLayer(self.point_layer)
        self.make_point_layer(self.last_clicked_loc[0], self.last_clicked_loc[1])

    def open_file_clicked(self, event):
        with wx.FileDialog(self, "Open files for geotagging", wildcard="Images (*.jpg)|*.jpg",
                       style=wx.FD_OPEN | wx.FD_MULTIPLE) as fileDialog:

            if fileDialog.ShowModal() == wx.ID_CANCEL:
                return     # the user changed their mind

            paths = fileDialog.GetPaths()
            for p in paths:
                self.file_list.InsertItem(0, p)

    def set_location_clicked(self, event):
        if self.last_clicked_loc == None:
            wx.MessageBox('Please right-click to set location first', 'Info', wx.OK | wx.ICON_INFORMATION)
            return
        
        for i in range(self.file_list.GetItemCount()):
            file_to_process = self.file_list.GetItemText(i)
            print(file_to_process)

            lng_deg = to_deg(self.last_clicked_loc[0], ["W", "E"])
            lat_deg = to_deg(self.last_clicked_loc[1], ["S", "N"])
            exiv_lng = (change_to_rational(lng_deg[0]), change_to_rational(lng_deg[1]), change_to_rational(lng_deg[2]))
            exiv_lat = (change_to_rational(lat_deg[0]), change_to_rational(lat_deg[1]), change_to_rational(lat_deg[2]))

            im = Image.open(file_to_process)
            exif_dict = piexif.load(im.info["exif"])
            exif_dict["GPS"] = {
                piexif.GPSIFD.GPSVersionID: (2, 0, 0, 0),
                piexif.GPSIFD.GPSAltitudeRef: 0,
                piexif.GPSIFD.GPSAltitude: (1, 1),
                piexif.GPSIFD.GPSLatitudeRef: lat_deg[3],
                piexif.GPSIFD.GPSLatitude: exiv_lat,
                piexif.GPSIFD.GPSLongitudeRef: lng_deg[3],
                piexif.GPSIFD.GPSLongitude: exiv_lng,
            }
            
            exif_bytes = piexif.dump(exif_dict)
            piexif.insert(exif_bytes, file_to_process)