~rosco2/ubuntu/wily/gramps/bug-1492304

« back to all changes in this revision

Viewing changes to .pc/introspection_geography.patch/gramps/plugins/lib/maps/geography.py

  • Committer: Package Import Robot
  • Author(s): Ross Gammon
  • Date: 2015-08-11 23:03:11 UTC
  • mfrom: (1.4.3)
  • mto: This revision was merged to the branch mainline in revision 58.
  • Revision ID: package-import@ubuntu.com-20150811230311-acjr8gcfe8isx7ij
* New upstream release
* Drop patches applied upstream or cherry-picked from there
* Add version constraints for gtk and pygobject
* Add goocanvas dependency - available soon
* Drop webkit dpendency as HTML view has been removed
* Force removal of upstream packages when installing Debian one
  (LP: #1464845)
* Drop fixperm override as permissions fixed upstream
* Fix spelling error in changelog
* Switch to nose for unit tests
* Add build dependencies for the nose tests
* Update copyright file
* Add uversionmangle to watch file to deal with alpha/beta versions
* Add manual test cases
* Drop FAQ URL from upstream metadata - changes every release
* Add patch to fix transparent windows in Ubuntu.
  Thanks to Lance Orner (LP: #1451259)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
# -*- python -*-
2
 
# -*- coding: utf-8 -*-
3
 
#
4
 
# Gramps - a GTK+/GNOME based genealogy program
5
 
#
6
 
# Copyright (C) 2011-2012       Serge Noiraud
7
 
#
8
 
# This program is free software; you can redistribute it and/or modify
9
 
# it under the terms of the GNU General Public License as published by
10
 
# the Free Software Foundation; either version 2 of the License, or
11
 
# (at your option) any later version.
12
 
#
13
 
# This program is distributed in the hope that it will be useful,
14
 
# but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 
# GNU General Public License for more details.
17
 
#
18
 
# You should have received a copy of the GNU General Public License
19
 
# along with this program; if not, write to the Free Software
20
 
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21
 
#
22
 
 
23
 
#-------------------------------------------------------------------------
24
 
#
25
 
# Python modules
26
 
#
27
 
#-------------------------------------------------------------------------
28
 
from gramps.gen.const import GRAMPS_LOCALE as glocale
29
 
_ = glocale.translation.sgettext
30
 
import os
31
 
import sys
32
 
import re
33
 
from gi.repository import GObject
34
 
import time
35
 
from gi.repository import GLib
36
 
from math import pi
37
 
 
38
 
#-------------------------------------------------------------------------
39
 
#
40
 
# GTK/Gnome modules
41
 
#
42
 
#-------------------------------------------------------------------------
43
 
from gi.repository import Gtk
44
 
from gi.repository import GdkPixbuf
45
 
import cairo
46
 
 
47
 
#-------------------------------------------------------------------------
48
 
#
49
 
# Gramps Modules
50
 
#
51
 
#-------------------------------------------------------------------------
52
 
from gramps.gen.lib import EventType, Place, PlaceType, PlaceRef
53
 
from gramps.gen.display.name import displayer as _nd
54
 
from gramps.gen.display.place import displayer as _pd
55
 
from gramps.gui.views.navigationview import NavigationView
56
 
from gramps.gen.utils.libformatting import FormattingHelper
57
 
from gramps.gen.errors import WindowActiveError
58
 
from gramps.gen.const import HOME_DIR, IMAGE_DIR
59
 
from gramps.gui.managedwindow import ManagedWindow
60
 
from gramps.gen.config import config
61
 
from gramps.gui.editors import EditPlace, EditEvent, EditFamily, EditPerson
62
 
from gramps.gui.selectors.selectplace import SelectPlace
63
 
from gramps.gen.constfunc import conv_to_unicode
64
 
 
65
 
from gi.repository import OsmGpsMap as osmgpsmap
66
 
from . import constants
67
 
from .osmgps import OsmGps
68
 
from .selectionlayer import SelectionLayer
69
 
from .placeselection import PlaceSelection
70
 
from .cairoprint import CairoPrintSave
71
 
 
72
 
#------------------------------------------------------------------------
73
 
#
74
 
# Set up logging
75
 
#
76
 
#------------------------------------------------------------------------
77
 
import logging
78
 
_LOG = logging.getLogger("maps.geography")
79
 
 
80
 
#-------------------------------------------------------------------------
81
 
#
82
 
# Constants
83
 
#
84
 
#-------------------------------------------------------------------------
85
 
GEOGRAPHY_PATH = os.path.join(HOME_DIR, "maps")
86
 
 
87
 
#-------------------------------------------------------------------------
88
 
#
89
 
# Functions and variables
90
 
#
91
 
#-------------------------------------------------------------------------
92
 
PLACE_REGEXP = re.compile('<span background="green">(.*)</span>')
93
 
PLACE_STRING = '<span background="green">%s</span>'
94
 
 
95
 
def _get_sign(value):
96
 
    """
97
 
    return 1 if we have a negative number, 0 in other case
98
 
    """
99
 
    if value < 0.0:
100
 
        return 1
101
 
    else:
102
 
        return 0
103
 
 
104
 
#-------------------------------------------------------------------------
105
 
#
106
 
# GeoGraphyView
107
 
#
108
 
#-------------------------------------------------------------------------
109
 
class GeoGraphyView(OsmGps, NavigationView):
110
 
    """
111
 
    View for pedigree tree.
112
 
    Displays the ancestors of a selected individual.
113
 
    """
114
 
    #settings in the config file
115
 
    CONFIGSETTINGS = (
116
 
        ('geography.path', GEOGRAPHY_PATH),
117
 
 
118
 
        ('geography.zoom', 10),
119
 
        ('geography.zoom_when_center', 12),
120
 
        ('geography.show_cross', True),
121
 
        ('geography.lock', False),
122
 
        ('geography.center-lat', 0.0),
123
 
        ('geography.center-lon', 0.0),
124
 
 
125
 
        ('geography.map_service', constants.OPENSTREETMAP),
126
 
        ('geography.max_places', 5000),
127
 
        ('geography.use-keypad', True),
128
 
        )
129
 
 
130
 
    def __init__(self, title, pdata, dbstate, uistate,
131
 
                 bm_type, nav_group):
132
 
        NavigationView.__init__(self, title, pdata, dbstate, uistate,
133
 
                                bm_type, nav_group)
134
 
 
135
 
        self.dbstate = dbstate
136
 
        self.dbstate.connect('database-changed', self.change_db)
137
 
        self.default_text = "Enter location here!"
138
 
        self.centerlon = config.get("geography.center-lon")
139
 
        self.centerlat = config.get("geography.center-lat")
140
 
        self.zoom = config.get("geography.zoom")
141
 
        self.lock = config.get("geography.lock")
142
 
        if config.get('geography.path') == "" :
143
 
            config.set('geography.path', GEOGRAPHY_PATH )
144
 
        OsmGps.__init__(self)
145
 
 
146
 
        self.format_helper = FormattingHelper(self.dbstate)
147
 
        self.centerlat = self.centerlon = 0.0
148
 
        self.cross_map = None
149
 
        self.current_map = None
150
 
        self.without = 0
151
 
        self.place_list = []
152
 
        self.places_found = []
153
 
        self.select_fct = None
154
 
        self.geo_mainmap = None
155
 
        path = os.path.join(IMAGE_DIR, "48x48",
156
 
                            ('gramps-geo-mainmap' + '.png' ))
157
 
        with open(path, 'rb') as fh: # this is to avoid bug with cairo 1.8.8
158
 
            self.geo_mainmap = cairo.ImageSurface.create_from_png(fh)
159
 
        #self.geo_mainmap = cairo.ImageSurface.create_from_png(path)
160
 
        path = os.path.join(IMAGE_DIR, "48x48",
161
 
                            ('gramps-geo-altmap' + '.png' ))
162
 
        with open(path, 'rb') as fh: # this is to avoid bug with cairo 1.8.8
163
 
            self.geo_altmap = cairo.ImageSurface.create_from_png(fh)
164
 
        #self.geo_altmap = cairo.ImageSurface.create_from_png(path)
165
 
        if ( config.get('geography.map_service') in
166
 
            ( constants.OPENSTREETMAP,
167
 
              constants.MAPS_FOR_FREE,
168
 
              constants.OPENCYCLEMAP,
169
 
              constants.OSM_PUBLIC_TRANSPORT,
170
 
             )):
171
 
            default_image = self.geo_mainmap
172
 
        else:
173
 
            default_image = self.geo_altmap
174
 
        self.geo_othermap = {}
175
 
        for ident in ( EventType.BIRTH,
176
 
                    EventType.DEATH,
177
 
                    EventType.MARRIAGE ):
178
 
            path = os.path.join(IMAGE_DIR, "48x48",
179
 
                                (constants.ICONS.get(int(ident), default_image) + '.png' ))
180
 
            with open(path, 'rb') as fh: # this is to avoid bug with cairo 1.8.8
181
 
                self.geo_othermap[ident] = cairo.ImageSurface.create_from_png(fh)
182
 
            #self.geo_othermap[ident] = cairo.ImageSurface.create_from_png(path)
183
 
 
184
 
    def add_bookmark(self, menu):
185
 
        mlist = self.selected_handles()
186
 
        if mlist:
187
 
            self.bookmarks.add(mlist[0])
188
 
        else:
189
 
            from gramps.gui.dialog import WarningDialog
190
 
            WarningDialog(
191
 
                _("Could Not Set a Bookmark"), 
192
 
                _("A bookmark could not be set because "
193
 
                  "no one was selected."))
194
 
        
195
 
 
196
 
    def add_bookmark_from_popup(self, menu, handle):
197
 
        if handle:
198
 
            self.uistate.set_active(handle, self.navigation_type())
199
 
            self.bookmarks.add(handle)
200
 
            self.bookmarks.redraw()
201
 
        else:
202
 
            from gramps.gui.dialog import WarningDialog
203
 
            WarningDialog(
204
 
                _("Could Not Set a Bookmark"), 
205
 
                _("A bookmark could not be set because "
206
 
                  "no one was selected."))
207
 
 
208
 
    def change_page(self):
209
 
        """
210
 
        Called when the page changes.
211
 
        """
212
 
        NavigationView.change_page(self)
213
 
        self.uistate.clear_filter_results()
214
 
        self.end_selection = None
215
 
        self.osm.grab_focus()
216
 
        self.set_crosshair(config.get("geography.show_cross"))
217
 
 
218
 
    def do_size_request(self, requisition):
219
 
        """
220
 
        Overridden method to handle size request events.
221
 
        """
222
 
        requisition.width = 400
223
 
        requisition.height = 300
224
 
 
225
 
    def do_get_preferred_width(self):
226
 
        """ GTK3 uses width for height sizing model. This method will 
227
 
            override the virtual method
228
 
        """
229
 
        req = Gtk.Requisition()
230
 
        self.do_size_request(req)
231
 
        return req.width, req.width
232
 
 
233
 
    def do_get_preferred_height(self):
234
 
        """ GTK3 uses width for height sizing model. This method will 
235
 
            override the virtual method
236
 
        """
237
 
        req = Gtk.Requisition()
238
 
        self.do_size_request(req)
239
 
        return req.height, req.height
240
 
 
241
 
    def on_delete(self):
242
 
        """
243
 
        Save all modified environment
244
 
        """
245
 
        NavigationView.on_delete(self)
246
 
        self._config.save()
247
 
 
248
 
    def change_db(self, dbse):
249
 
        """
250
 
        Callback associated with DbState. Whenever the database
251
 
        changes, this task is called. In this case, we rebuild the
252
 
        columns, and connect signals to the connected database. Tree
253
 
        is no need to store the database, since we will get the value
254
 
        from self.state.db
255
 
        """
256
 
        if self.active:
257
 
            self.bookmarks.redraw()
258
 
        self.build_tree()
259
 
        self.osm.grab_focus()
260
 
        self.set_crosshair(config.get("geography.show_cross"))
261
 
 
262
 
    def can_configure(self):
263
 
        """
264
 
        See :class:`~gui.views.pageview.PageView
265
 
        :return: bool
266
 
        """
267
 
        return True
268
 
 
269
 
    def define_actions(self):
270
 
        """
271
 
        Required define_actions function for PageView. Builds the action
272
 
        group information required.
273
 
        As this function is overriden in some plugins, we need to call
274
 
        another method.
275
 
        """
276
 
        NavigationView.define_actions(self)
277
 
        self.define_print_actions()
278
 
 
279
 
    def define_print_actions(self):
280
 
        """
281
 
        Associate the print button to the PrintView action.
282
 
        """
283
 
        self._add_action('PrintView', Gtk.STOCK_PRINT, _("_Print..."), 
284
 
                         accel="<PRIMARY>P", 
285
 
                         tip=_("Print or save the Map"), 
286
 
                         callback=self.printview)
287
 
 
288
 
    def config_connect(self):
289
 
        """
290
 
        Overwriten from  :class:`~gui.views.pageview.PageView method
291
 
        This method will be called after the ini file is initialized,
292
 
        use it to monitor changes in the ini file
293
 
        """
294
 
        self._config.connect("geography.path",
295
 
                          self.set_path)
296
 
        self._config.connect("geography.zoom_when_center",
297
 
                          self.set_zoom_when_center)
298
 
 
299
 
    def set_path(self, client, cnxn_id, entry, data):
300
 
        """
301
 
        All geography views must have the same path for maps
302
 
        """
303
 
        config.set("geography.path", entry)
304
 
 
305
 
    def set_zoom_when_center(self, client, cnxn_id, entry, data):
306
 
        """
307
 
        All geography views must have the same zoom_when_center for maps
308
 
        """
309
 
        config.set("geography.zoom_when_center", int(entry))
310
 
 
311
 
    #-------------------------------------------------------------------------
312
 
    #
313
 
    # Map Menu
314
 
    #
315
 
    #-------------------------------------------------------------------------
316
 
    def build_nav_menu(self, obj, event, lat, lon):
317
 
        """
318
 
        Builds the menu for actions on the map.
319
 
        """
320
 
        self.menu = Gtk.Menu()
321
 
        menu = self.menu
322
 
        menu.set_title(_('Map Menu'))
323
 
 
324
 
        if config.get("geography.show_cross"):
325
 
            title = _('Remove cross hair')
326
 
        else:
327
 
            title = _('Add cross hair')
328
 
        add_item = Gtk.MenuItem(label=title)
329
 
        add_item.connect("activate", self.config_crosshair, event, lat , lon)
330
 
        add_item.show()
331
 
        menu.append(add_item)
332
 
 
333
 
        if config.get("geography.lock"):
334
 
            title = _('Unlock zoom and position')
335
 
        else:
336
 
            title = _('Lock zoom and position')
337
 
        add_item = Gtk.MenuItem(label=title)
338
 
        add_item.connect("activate", self.config_zoom_and_position,
339
 
                         event, lat , lon)
340
 
        add_item.show()
341
 
        menu.append(add_item)
342
 
 
343
 
        add_item = Gtk.MenuItem(label=_("Add place"))
344
 
        add_item.connect("activate", self.add_place, event, lat , lon)
345
 
        add_item.show()
346
 
        menu.append(add_item)
347
 
 
348
 
        add_item = Gtk.MenuItem(label=_("Link place"))
349
 
        add_item.connect("activate", self.link_place, event, lat , lon)
350
 
        add_item.show()
351
 
        menu.append(add_item)
352
 
 
353
 
        add_item = Gtk.MenuItem(label=_("Center here"))
354
 
        add_item.connect("activate", self.set_center, event, lat , lon)
355
 
        add_item.show()
356
 
        menu.append(add_item)
357
 
 
358
 
        # Add specific module menu
359
 
        self.add_specific_menu(menu, event, lat, lon)
360
 
        # Add a separator line
361
 
        add_item = Gtk.MenuItem()
362
 
        add_item.show()
363
 
        menu.append(add_item)
364
 
 
365
 
        map_name = constants.map_title[config.get("geography.map_service")]
366
 
        title = _("Replace '%(map)s' by =>") % {
367
 
                   'map' : map_name
368
 
                  }
369
 
        add_item = Gtk.MenuItem(label=title)
370
 
        add_item.show()
371
 
        menu.append(add_item)
372
 
 
373
 
        self.changemap = Gtk.Menu()
374
 
        changemap = self.changemap
375
 
        changemap.set_title(title)
376
 
        changemap.show()
377
 
        add_item.set_submenu(changemap)
378
 
        # show in the map menu all available providers
379
 
        for map in constants.map_type:
380
 
            changemapitem = Gtk.MenuItem(label=constants.map_title[map])
381
 
            changemapitem.show()
382
 
            changemapitem.connect("activate", self.change_map, map)
383
 
            changemap.append(changemapitem)
384
 
 
385
 
        clear_text = _("Clear the '%(map)s' tiles cache.") % {
386
 
                   'map' : map_name
387
 
                  }
388
 
        self.clearmap = Gtk.MenuItem(label=clear_text)
389
 
        clearmap = self.clearmap
390
 
        clearmap.connect("activate", self.clear_map, constants.tiles_path[config.get("geography.map_service")])
391
 
 
392
 
        clearmap.show()
393
 
        menu.append(clearmap)
394
 
        menu.show()
395
 
        menu.popup(None, None,
396
 
                   lambda menu, data: (event.get_root_coords()[0],
397
 
                                       event.get_root_coords()[1], True),
398
 
                   None, event.button, event.time)
399
 
        return 1
400
 
 
401
 
 
402
 
    def clear_map(self, menu, the_map):
403
 
        """
404
 
        We need to clean the tiles cache for the current map
405
 
        """
406
 
        import shutil
407
 
     
408
 
        path = "%s%c%s" % ( config.get('geography.path'), os.sep, the_map )
409
 
        shutil.rmtree(path)
410
 
        pass
411
 
 
412
 
    def add_specific_menu(self, menu, event, lat, lon):
413
 
        """
414
 
        Add specific entry to the navigation menu.
415
 
        Must be done in the associated menu.
416
 
        """
417
 
        raise NotImplementedError
418
 
 
419
 
    def set_center(self, menu, event, lat, lon):
420
 
        """
421
 
        Center the map at the new position then save it.
422
 
        """
423
 
        self.osm.set_center_and_zoom(lat, lon,
424
 
                                     config.get("geography.zoom_when_center"))
425
 
        self.save_center(lat, lon)
426
 
 
427
 
    #-------------------------------------------------------------------------
428
 
    #
429
 
    # Markers management
430
 
    #
431
 
    #-------------------------------------------------------------------------
432
 
    def is_there_a_marker_here(self, event, lat, lon):
433
 
        """
434
 
        Is there a marker at this position ?
435
 
        """
436
 
        found = False
437
 
        mark_selected = []
438
 
        self.uistate.set_busy_cursor(True)
439
 
        for mark in self.sort:
440
 
            # as we are not precise with our hand, reduce the precision
441
 
            # depending on the zoom.
442
 
            precision = {
443
 
                          1 : '%3.0f', 2 : '%3.1f', 3 : '%3.1f', 4 : '%3.1f',
444
 
                          5 : '%3.2f', 6 : '%3.2f', 7 : '%3.2f', 8 : '%3.3f',
445
 
                          9 : '%3.3f', 10 : '%3.3f', 11 : '%3.3f', 12 : '%3.3f',
446
 
                         13 : '%3.3f', 14 : '%3.4f', 15 : '%3.4f', 16 : '%3.4f',
447
 
                         17 : '%3.4f', 18 : '%3.4f'
448
 
                         }.get(config.get("geography.zoom"), '%3.1f')
449
 
            shift = {
450
 
                          1 : 5.0, 2 : 5.0, 3 : 3.0,
451
 
                          4 : 1.0, 5 : 0.5, 6 : 0.3, 7 : 0.15,
452
 
                          8 : 0.06, 9 : 0.03, 10 : 0.015,
453
 
                         11 : 0.005, 12 : 0.003, 13 : 0.001,
454
 
                         14 : 0.0005, 15 : 0.0003, 16 : 0.0001,
455
 
                         17 : 0.0001, 18 : 0.0001
456
 
                         }.get(config.get("geography.zoom"), 5.0)
457
 
            latp  = precision % lat
458
 
            lonp  = precision % lon
459
 
            mlatp = precision % float(mark[3])
460
 
            mlonp = precision % float(mark[4])
461
 
            latok = lonok = False
462
 
            _LOG.debug(" compare latitude : %s with %s (precision = %s)"
463
 
                       " place='%s'" % (float(mark[3]), lat, precision,
464
 
                                        mark[0]))
465
 
            _LOG.debug("compare longitude : %s with %s (precision = %s)"
466
 
                       " zoom=%d" % (float(mark[4]), lon, precision,
467
 
                                     config.get("geography.zoom")))
468
 
            if (float(mlatp) >= (float(latp) - shift) ) and \
469
 
               (float(mlatp) <= (float(latp) + shift) ):
470
 
                latok = True
471
 
            if (float(mlonp) >= (float(lonp) - shift) ) and \
472
 
               (float(mlonp) <= (float(lonp) + shift) ):
473
 
                lonok = True
474
 
            if latok and lonok:
475
 
                mark_selected.append(mark)
476
 
                found = True
477
 
        if found:
478
 
            self.bubble_message(event, lat, lon, mark_selected)
479
 
        self.uistate.set_busy_cursor(False)
480
 
 
481
 
    def bubble_message(self, event, lat, lon, mark):
482
 
        """
483
 
        Display the bubble message. depends on the view.
484
 
        """
485
 
        raise NotImplementedError
486
 
 
487
 
    def add_selection_layer(self):
488
 
        """
489
 
        add the selection layer
490
 
        """
491
 
        selection_layer = SelectionLayer()
492
 
        self.osm.layer_add(selection_layer)
493
 
        return selection_layer
494
 
 
495
 
    def remove_layer(self, layer):
496
 
        """
497
 
        Remove the specified layer
498
 
        """
499
 
        self.osm.remove_layer(layer)
500
 
 
501
 
    def add_marker(self, menu, event, lat, lon, event_type, differtype, count):
502
 
        """
503
 
        Add a new marker
504
 
        """
505
 
        mapservice = config.get('geography.map_service')
506
 
        if ( mapservice in ( constants.OPENSTREETMAP,
507
 
                             constants.OPENSTREETMAP_RENDERER )):
508
 
            default_image = self.geo_mainmap
509
 
        else:
510
 
            default_image = self.geo_altmap
511
 
        value = default_image
512
 
        if event_type is not None:
513
 
            value = self.geo_othermap.get(int(event_type), default_image)
514
 
        if differtype:                   # in case multiple evts
515
 
            value = default_image # we use default icon.
516
 
        self.marker_layer.add_marker((float(lat), float(lon)), value, count)
517
 
 
518
 
    def remove_all_gps(self):
519
 
        """
520
 
        Remove all gps points on the map
521
 
        """
522
 
        self.osm.gps_clear()
523
 
 
524
 
    def remove_all_tracks(self):
525
 
        """
526
 
        Remove all tracks on the map
527
 
        """
528
 
        self.osm.track_remove_all()
529
 
 
530
 
    def remove_all_markers(self):
531
 
        """
532
 
        Remove all markers on the map
533
 
        """
534
 
        self.marker_layer.clear_markers()
535
 
 
536
 
    def _present_in_places_list(self, index, string):
537
 
        """
538
 
        Search a string in place_list depending index
539
 
        """
540
 
        found = any(p[index] == string for p in self.place_list)
541
 
        return found
542
 
 
543
 
    def _append_to_places_list(self, place, evttype, name, lat,
544
 
                               longit, descr, year, icontype,
545
 
                               gramps_id, place_id, event_id, family_id
546
 
                              ):
547
 
        """
548
 
        Create a list of places with coordinates.
549
 
        """
550
 
        found = any(p[0] == place for p in self.places_found)
551
 
        if not found and self.nbplaces < self._config.get("geography.max_places"):
552
 
            # We only show the first "geography.max_places".
553
 
            # over 3000 or 4000 places, the geography become unusable.
554
 
            # In this case, filter the places ...
555
 
            self.nbplaces += 1
556
 
            self.places_found.append([place, lat, longit])
557
 
        self.place_list.append([place, name, evttype, lat,
558
 
                                longit, descr, year, icontype,
559
 
                                gramps_id, place_id, event_id, family_id
560
 
                               ])
561
 
        self.nbmarkers += 1
562
 
        tfa = float(lat)
563
 
        tfb = float(longit)
564
 
        if year is not None:
565
 
            tfc = int(year)
566
 
            if tfc != 0:
567
 
                if tfc < self.minyear:
568
 
                    self.minyear = tfc
569
 
                if tfc > self.maxyear:
570
 
                    self.maxyear = tfc
571
 
        tfa += 0.00000001 if tfa >= 0 else -0.00000001
572
 
        tfb += 0.00000001 if tfb >= 0 else -0.00000001
573
 
        if self.minlat == 0.0 or tfa < self.minlat:
574
 
            self.minlat = tfa
575
 
        if self.maxlat == 0.0 or tfa > self.maxlat:
576
 
            self.maxlat = tfa
577
 
        if self.minlon == 0.0 or tfb < self.minlon:
578
 
            self.minlon = tfb
579
 
        if self.maxlon == 0.0 or tfb > self.maxlon:
580
 
            self.maxlon = tfb
581
 
 
582
 
    def _append_to_places_without_coord(self, gid, place):
583
 
        """
584
 
        Create a list of places without coordinates.
585
 
        """
586
 
        if not [gid, place] in self.place_without_coordinates:
587
 
            self.place_without_coordinates.append([gid, place])
588
 
            self.without += 1
589
 
 
590
 
    def _create_markers(self):
591
 
        """
592
 
        Create all markers for the specified person.
593
 
        """
594
 
        self.remove_all_markers()
595
 
        self.remove_all_gps()
596
 
        self.remove_all_tracks()
597
 
        if ( self.current_map is not None and
598
 
             self.current_map != config.get("geography.map_service") ):
599
 
            self.change_map(self.osm, config.get("geography.map_service"))
600
 
        last = ""
601
 
        current = ""
602
 
        differtype = False
603
 
        savetype = None
604
 
        lat = 0.0
605
 
        lon = 0.0
606
 
        icon = None
607
 
        count = 0
608
 
        self.uistate.set_busy_cursor(True)
609
 
        _LOG.debug("%s" % time.strftime("start create_marker : "
610
 
                   "%a %d %b %Y %H:%M:%S", time.gmtime()))
611
 
        for mark in self.sort:
612
 
            current = ([mark[3], mark[4]])
613
 
            if last == "":
614
 
                last = current
615
 
                lat = mark[3]
616
 
                lon = mark[4]
617
 
                icon = mark[7]
618
 
                differtype = False
619
 
                count = 1
620
 
                continue
621
 
            if last != current:
622
 
                self.add_marker(None, None, lat, lon, icon, differtype, count)
623
 
                differtype = False
624
 
                count = 1
625
 
                last = current
626
 
                lat = mark[3]
627
 
                lon = mark[4]
628
 
                icon = mark[7]
629
 
            else: # This marker already exists. add info.
630
 
                count += 1
631
 
                if icon != mark[7]:
632
 
                    differtype = True
633
 
        if ( lat != 0.0 and lon != 0.0 ):
634
 
            self.add_marker(None, None, lat, lon, icon, differtype, count)
635
 
            self._set_center_and_zoom()
636
 
        _LOG.debug("%s" % time.strftime(" stop create_marker : "
637
 
                   "%a %d %b %Y %H:%M:%S", time.gmtime()))
638
 
        self.uistate.set_busy_cursor(False)
639
 
 
640
 
    def _visible_marker(self, lat, lon):
641
 
        """
642
 
        Is this marker in the visible area ?
643
 
        """
644
 
        bbox = self.osm.get_bbox()
645
 
        s_lon = lon + 10.0
646
 
        s_lat = lat + 10.0
647
 
        pt1 = bbox[0]
648
 
        s_bbox_lat1 = pt1.rlat + 10.0
649
 
        s_bbox_lon1 = pt1.rlon + 10.0
650
 
        pt2 = bbox[1]
651
 
        s_bbox_lat2 = pt2.rlat + 10.0
652
 
        s_bbox_lon2 = pt2.rlon + 10.0
653
 
        result = ( s_bbox_lat1 > s_lat > s_bbox_lat2 ) and \
654
 
                 ( s_bbox_lon1 < s_lon < s_bbox_lon2 )
655
 
        return result
656
 
 
657
 
    def _autozoom_in(self, lvl, p1lat, p1lon, p2lat, p2lon):
658
 
        """
659
 
        We zoom in until at least one marker missing.
660
 
        """
661
 
        if ( ( self._visible_marker(p1lat, p1lon)
662
 
                  and self._visible_marker(p2lat, p2lon) )
663
 
                and lvl < 18 ):
664
 
            lvl += 1
665
 
            self.osm.set_zoom(lvl)
666
 
            GLib.timeout_add(int(50), self._autozoom_in, lvl,
667
 
                                p1lat, p1lon, p2lat, p2lon)
668
 
        else:
669
 
            GLib.timeout_add(int(50), self._autozoom_out, lvl,
670
 
                                p1lat, p1lon, p2lat, p2lon)
671
 
 
672
 
    def _autozoom_out(self, lvl, p1lat, p1lon, p2lat, p2lon):
673
 
        """
674
 
        We zoom out until all markers visible.
675
 
        """
676
 
        if ( not ( self._visible_marker(p1lat, p1lon)
677
 
                      and self._visible_marker(p2lat, p2lon) )
678
 
                and lvl > 1 ):
679
 
            lvl -= 1
680
 
            self.osm.set_zoom(lvl)
681
 
            GLib.timeout_add(int(50), self._autozoom_out, lvl,
682
 
                                p1lat, p1lon, p2lat, p2lon)
683
 
        else:
684
 
            layer = self.get_selection_layer()
685
 
            if layer:
686
 
                self.osm.layer_remove(layer)
687
 
 
688
 
    def _autozoom(self):
689
 
        """
690
 
        Try to put all markers on the map.  we start at current zoom.
691
 
        If all markers are present, continue to zoom.
692
 
        If some markers are missing : return to the zoom - 1
693
 
        We must use function called by timeout to force map updates.
694
 
        """
695
 
        level_start = self.osm.props.zoom
696
 
        p1lat, p1lon = self.begin_selection.get_degrees()
697
 
        p2lat, p2lon = self.end_selection.get_degrees()
698
 
        lat = p1lat + ( p2lat - p1lat ) / 2
699
 
        lon = p1lon + ( p2lon - p1lon ) / 2
700
 
        # We center the map on the center of the region
701
 
        self.osm.set_center(lat, lon)
702
 
        self.save_center(lat, lon)
703
 
        p1lat = self.begin_selection.rlat
704
 
        p1lon = self.begin_selection.rlon
705
 
        p2lat = self.end_selection.rlat
706
 
        p2lon = self.end_selection.rlon
707
 
        # We zoom in until at least one marker missing.
708
 
        GLib.timeout_add(int(50), self._autozoom_in, level_start,
709
 
                            p1lat, p1lon, p2lat, p2lon)
710
 
 
711
 
    def _set_center_and_zoom(self):
712
 
        """
713
 
        Calculate the zoom.
714
 
        The best should be an auto zoom to have all markers on the screen.
715
 
        need some works here.
716
 
        we start at zoom 1 until zoom y ( for this a preference )
717
 
        If all markers are present, continue to zoom.
718
 
        If some markers are missing : return to the zoom - 1
719
 
        The following is too complex. In some case, all markers are not present.
720
 
        """
721
 
        # Select the center of the map and the zoom
722
 
        signminlon = _get_sign(self.minlon)
723
 
        signminlat = _get_sign(self.minlat)
724
 
        signmaxlon = _get_sign(self.maxlon)
725
 
        signmaxlat = _get_sign(self.maxlat)
726
 
        current = osmgpsmap.MapPoint.new_degrees(self.minlat, self.minlon)
727
 
        self.end_selection = current 
728
 
        current = osmgpsmap.MapPoint.new_degrees(self.maxlat, self.maxlon)
729
 
        self.begin_selection = current 
730
 
        if signminlon == signmaxlon:
731
 
            maxlong = abs(abs(self.minlon) - abs(self.maxlon))
732
 
        else:
733
 
            maxlong = abs(abs(self.minlon) + abs(self.maxlon))
734
 
        if signminlat == signmaxlat:
735
 
            maxlat = abs(abs(self.minlat) - abs(self.maxlat))
736
 
        else:
737
 
            maxlat = abs(abs(self.minlat) + abs(self.maxlat))
738
 
        latit = longt = 0.0
739
 
        for mark in self.sort:
740
 
            if ( signminlat == signmaxlat ):
741
 
                if signminlat == 1:
742
 
                    latit = self.minlat+self.centerlat
743
 
                else:
744
 
                    latit = self.maxlat-self.centerlat
745
 
            elif self.maxlat > self.centerlat:
746
 
                latit = self.maxlat-self.centerlat
747
 
            else:
748
 
                latit = self.minlat+self.centerlat
749
 
            if ( signminlon == signmaxlon ):
750
 
                if signminlon == 1:
751
 
                    longt = self.minlon+self.centerlon
752
 
                else:
753
 
                    longt = self.maxlon-self.centerlon
754
 
            elif self.maxlon > self.centerlon:
755
 
                longt = self.maxlon-self.centerlon
756
 
            else:
757
 
                longt = self.minlon+self.centerlon
758
 
            # all maps: 0.0 for longitude and latitude means no location.
759
 
            if latit == longt == 0.0:
760
 
                latit = longt = 0.00000001
761
 
        self.latit = latit
762
 
        self.longt = longt
763
 
        if config.get("geography.lock"):
764
 
            self.osm.set_center_and_zoom(config.get("geography.center-lat"),
765
 
                                         config.get("geography.center-lon"),
766
 
                                         config.get("geography.zoom") )
767
 
        else:
768
 
            self._autozoom()
769
 
            self.save_center(self.latit, self.longt)
770
 
            config.set("geography.zoom", self.osm.props.zoom)
771
 
        self.end_selection = None
772
 
 
773
 
    def _get_father_and_mother_name(self, event):
774
 
        """
775
 
        Return the father and mother name of a family event
776
 
        """
777
 
        dbstate = self.dbstate
778
 
        family_list = [
779
 
            dbstate.db.get_family_from_handle(ref_handle)
780
 
            for (ref_type, ref_handle) in
781
 
                dbstate.db.find_backlink_handles(event.handle)
782
 
                    if ref_type == 'Family'
783
 
                      ]
784
 
        fnam = mnam = _("Unknown")
785
 
        if family_list:
786
 
            for family in family_list:
787
 
                handle = family.get_father_handle()
788
 
                father = dbstate.db.get_person_from_handle(handle)
789
 
                handle = family.get_mother_handle()
790
 
                mother = dbstate.db.get_person_from_handle(handle)
791
 
                fnam = _nd.display(father) if father else _("Unknown")
792
 
                mnam = _nd.display(mother) if mother else _("Unknown")
793
 
        return ( fnam, mnam )
794
 
 
795
 
    #-------------------------------------------------------------------------
796
 
    #
797
 
    # Printing functionalities
798
 
    #
799
 
    #-------------------------------------------------------------------------
800
 
    def printview(self, obj):
801
 
        """
802
 
        Print or save the view that is currently shown
803
 
        """
804
 
        req = self.osm.get_allocation()
805
 
        widthpx = req.width
806
 
        heightpx = req.height
807
 
        prt = CairoPrintSave(widthpx, heightpx, self.osm.do_draw, self.osm)
808
 
        prt.run()
809
 
 
810
 
    #-------------------------------------------------------------------------
811
 
    #
812
 
    # Specific functionalities
813
 
    #
814
 
    #-------------------------------------------------------------------------
815
 
    def center_here(self, menu, event, lat, lon, mark):
816
 
        """
817
 
        Center the map at the marker position
818
 
        """
819
 
        self.set_center(menu, event, float(mark[3]), float(mark[4]))
820
 
 
821
 
    def add_place_bubble_message(self, event, lat, lon, marks,
822
 
                                 menu, message, mark):
823
 
        """
824
 
        Create the place menu of a marker
825
 
        """
826
 
        add_item = Gtk.MenuItem()
827
 
        add_item.show()
828
 
        menu.append(add_item)
829
 
        add_item = Gtk.MenuItem(label=message)
830
 
        add_item.show()
831
 
        menu.append(add_item)
832
 
        self.itemoption = Gtk.Menu()
833
 
        itemoption = self.itemoption
834
 
        itemoption.set_title(message)
835
 
        itemoption.show()
836
 
        add_item.set_submenu(itemoption)
837
 
        modify = Gtk.MenuItem(label=_("Edit Place"))
838
 
        modify.show()
839
 
        modify.connect("activate", self.edit_place, event, lat, lon, mark)
840
 
        itemoption.append(modify)
841
 
        center = Gtk.MenuItem(label=_("Center on this place"))
842
 
        center.show()
843
 
        center.connect("activate", self.center_here, event, lat, lon, mark)
844
 
        itemoption.append(center)
845
 
        add_item = Gtk.MenuItem()
846
 
        add_item.show()
847
 
        menu.append(add_item)
848
 
 
849
 
    def edit_place(self, menu, event, lat, lon, mark):
850
 
        """
851
 
        Edit the selected place at the marker position
852
 
        """
853
 
        self.mark = mark
854
 
        place = self.dbstate.db.get_place_from_gramps_id(self.mark[9])
855
 
        parent_list = place.get_placeref_list()
856
 
        if len(parent_list) > 0:
857
 
            parent = parent_list[0].ref
858
 
        else:
859
 
            parent = None
860
 
        self.select_fct = PlaceSelection(self.uistate, self.dbstate, self.osm,
861
 
                       self.selection_layer, self.place_list,
862
 
                       lat, lon, self.__edit_place, parent)
863
 
 
864
 
    def edit_person(self, menu, event, lat, lon, mark):
865
 
        """
866
 
        Edit the selected person at the marker position
867
 
        """
868
 
        _LOG.debug("edit_person : %s" % mark[8])
869
 
        # need to add code here to edit the person.
870
 
        person = self.dbstate.db.get_person_from_gramps_id(mark[8])
871
 
        try:
872
 
            EditPerson(self.dbstate, self.uistate, [], person)
873
 
        except WindowActiveError:
874
 
            pass
875
 
 
876
 
    def edit_family(self, menu, event, lat, lon, mark):
877
 
        """
878
 
        Edit the selected family at the marker position
879
 
        """
880
 
        _LOG.debug("edit_family : %s" % mark[11])
881
 
        family = self.dbstate.db.get_family_from_gramps_id(mark[11])
882
 
        try:
883
 
            EditFamily(self.dbstate, self.uistate, [], family)
884
 
        except WindowActiveError:
885
 
            pass
886
 
 
887
 
    def edit_event(self, menu, event, lat, lon, mark):
888
 
        """
889
 
        Edit the selected event at the marker position
890
 
        """
891
 
        _LOG.debug("edit_event : %s" % mark[10])
892
 
        event = self.dbstate.db.get_event_from_gramps_id(mark[10])
893
 
        try:
894
 
            EditEvent(self.dbstate, self.uistate, [], event)
895
 
        except WindowActiveError:
896
 
            pass
897
 
 
898
 
    def add_place(self, menu, event, lat, lon):
899
 
        """
900
 
        Add a new place using longitude and latitude of location centered
901
 
        on the map
902
 
        """
903
 
        self.select_fct = PlaceSelection(self.uistate, self.dbstate, self.osm,
904
 
                       self.selection_layer, self.place_list,
905
 
                       lat, lon, self.__add_place)
906
 
 
907
 
    def link_place(self, menu, event, lat, lon):
908
 
        """
909
 
        Link an existing place using longitude and latitude of location centered
910
 
        on the map
911
 
        If we have a place history, we must show all places to avoid an empty
912
 
        place selection in the PlaceSelection.
913
 
        """
914
 
        if self.uistate.get_active('Place'):
915
 
            self._createmap(None)
916
 
        selector = SelectPlace(self.dbstate, self.uistate, [])
917
 
        place = selector.run()
918
 
        if place:
919
 
            parent_list = place.get_placeref_list()
920
 
            if len(parent_list) > 0:
921
 
                parent = parent_list[0].ref
922
 
            else:
923
 
                parent = None
924
 
            places_handle = self.dbstate.db.iter_place_handles()
925
 
            nb_places = 0
926
 
            gids = ""
927
 
            place_title = _pd.display(self.dbstate.db, place)
928
 
            for place_hdl in places_handle:
929
 
                plce = self.dbstate.db.get_place_from_handle(place_hdl)
930
 
                plce_title = _pd.display(self.dbstate.db, plce)
931
 
                if plce_title == place_title:
932
 
                    nb_places += 1
933
 
                    if gids == "":
934
 
                        gids = plce.gramps_id
935
 
                    else:
936
 
                        gids = gids + ", " + plce.gramps_id
937
 
            if nb_places > 1:
938
 
                from gramps.gui.dialog import WarningDialog
939
 
                WarningDialog(
940
 
                      _('You have at least two places with the same title.'),
941
 
                      _("The title of the places is :\n"
942
 
                        "<b>%(title)s</b>\n"
943
 
                        "The following places are similar : %(gid)s\n"
944
 
                        "Eiher you rename the places either you merge them."
945
 
                        "\n\n<b>I can't proceed your request</b>.\n") % {
946
 
                                              'title': place_title,
947
 
                                              'gid': gids}
948
 
                      )
949
 
            else:
950
 
                self.mark = [None, None, None, None, None, None, None,
951
 
                             None, None, place.gramps_id, None, None]
952
 
                self.select_fct = PlaceSelection(self.uistate,
953
 
                                                 self.dbstate,
954
 
                                                 self.osm,
955
 
                                                 self.selection_layer,
956
 
                                                 self.place_list,
957
 
                                                 lat,
958
 
                                                 lon,
959
 
                                                 self.__edit_place,
960
 
                                                 parent)
961
 
 
962
 
    def __add_place(self, parent, plat, plon):
963
 
        """
964
 
        Add a new place using longitude and latitude of location centered
965
 
        on the map
966
 
        """
967
 
        self.select_fct.close()
968
 
        new_place = Place()
969
 
        new_place.set_latitude(str(plat))
970
 
        new_place.set_longitude(str(plon))
971
 
        if parent:
972
 
            placeref = PlaceRef()
973
 
            placeref.ref = parent
974
 
            new_place.add_placeref(placeref)
975
 
        try:
976
 
            EditPlace(self.dbstate, self.uistate, [], new_place)
977
 
            self.add_marker(None, None, plat, plon, None, True, 0)
978
 
        except WindowActiveError:
979
 
            pass
980
 
 
981
 
    def __edit_place(self, parent, plat, plon):
982
 
        """
983
 
        Edit the selected place at the marker position
984
 
        """
985
 
        self.select_fct.close()
986
 
        place = self.dbstate.db.get_place_from_gramps_id(self.mark[9])
987
 
        place.set_latitude(str(plat))
988
 
        place.set_longitude(str(plon))
989
 
        if parent:
990
 
            placeref = PlaceRef()
991
 
            placeref.ref = parent
992
 
            place.add_placeref(placeref)
993
 
        try:
994
 
            EditPlace(self.dbstate, self.uistate, [], place)
995
 
        except WindowActiveError:
996
 
            pass
997
 
 
998
 
    def __link_place(self, parent, plat, plon):
999
 
        """
1000
 
        Link an existing place using longitude and latitude of location centered
1001
 
        on the map
1002
 
        """
1003
 
        selector = SelectPlace(self.dbstate, self.uistate, [])
1004
 
        place = selector.run()
1005
 
        if place:
1006
 
            self.select_fct.close()
1007
 
            place.set_latitude(str(plat))
1008
 
            place.set_longitude(str(plon))
1009
 
            if parent:
1010
 
                placeref = PlaceRef()
1011
 
                placeref.ref = parent
1012
 
                place.add_placeref(placeref)
1013
 
            try:
1014
 
                EditPlace(self.dbstate, self.uistate, [], place)
1015
 
                self.add_marker(None, None, plat, plon, None, True, 0)
1016
 
            except WindowActiveError:
1017
 
                pass
1018
 
 
1019
 
    #-------------------------------------------------------------------------
1020
 
    #
1021
 
    # Geography preferences
1022
 
    #
1023
 
    #-------------------------------------------------------------------------
1024
 
    def _get_configure_page_funcs(self):
1025
 
        """
1026
 
        The function which is used to create the configuration window.
1027
 
        """
1028
 
        return [self.map_options, self.specific_options]
1029
 
 
1030
 
    def config_zoom_and_position(self, client, cnxn_id, entry, data):
1031
 
        """
1032
 
        Do we need to lock the zoom and position ?
1033
 
        """
1034
 
        if config.get("geography.lock"):
1035
 
            config.set("geography.lock", False)
1036
 
            self._set_center_and_zoom()
1037
 
        else:
1038
 
            config.set("geography.lock", True)
1039
 
        self.lock = config.get("geography.lock")
1040
 
        pass
1041
 
 
1042
 
    def config_crosshair(self, client, cnxn_id, entry, data):
1043
 
        """
1044
 
        We asked to change the crosshair.
1045
 
        """
1046
 
        if config.get("geography.show_cross"):
1047
 
            config.set("geography.show_cross", False)
1048
 
        else:
1049
 
            config.set("geography.show_cross", True)
1050
 
        self.set_crosshair(config.get("geography.show_cross"))
1051
 
        pass
1052
 
 
1053
 
    def specific_options(self, configdialog):
1054
 
        """
1055
 
        Add specific entry to the preference menu.
1056
 
        Must be done in the associated view.
1057
 
        """
1058
 
        table = Gtk.Table(n_rows=2, n_columns=2)
1059
 
        table.set_border_width(12)
1060
 
        table.set_col_spacings(6)
1061
 
        table.set_row_spacings(6)
1062
 
        configdialog.add_text(table, _('Nothing for this view.'), 0)
1063
 
        return _('Specific parameters'), table
1064
 
 
1065
 
    def map_options(self, configdialog):
1066
 
        """
1067
 
        Function that builds the widget in the configuration dialog
1068
 
        for the map options.
1069
 
        """
1070
 
        self._config.set('geography.path', config.get('geography.path'))
1071
 
        self._config.set('geography.zoom_when_center',
1072
 
                         config.get('geography.zoom_when_center'))
1073
 
        self._config.set('geography.max_places',
1074
 
                         self._config.get('geography.max_places'))
1075
 
        table = Gtk.Table(n_rows=1, n_columns=1)
1076
 
        table.set_border_width(12)
1077
 
        table.set_col_spacings(6)
1078
 
        table.set_row_spacings(6)
1079
 
        self.path_entry = Gtk.Entry()
1080
 
        configdialog.add_path_box(table,
1081
 
                _('Where to save the tiles for offline mode.'),
1082
 
                0, self.path_entry, config.get('geography.path'),
1083
 
                self.set_tilepath, self.select_tilepath)
1084
 
        configdialog.add_text(table,
1085
 
                _('If you have no more space in your file system. '
1086
 
                  'You can remove all tiles placed in the above path.\n'
1087
 
                  'Be careful! If you have no internet, you\'ll get no map.'),
1088
 
                2, line_wrap=False)
1089
 
        configdialog.add_slider(table,
1090
 
                _('Zoom used when centering'),
1091
 
                3, 'geography.zoom_when_center',
1092
 
                (2, 16))
1093
 
        configdialog.add_slider(table,
1094
 
                _('The maximum number of places to show'),
1095
 
                4, 'geography.max_places',
1096
 
                (1000, 10000))
1097
 
        configdialog.add_checkbox(table, 
1098
 
                _('Use keypad for shortcuts :\n' 
1099
 
                  'Either we choose the + and - from the keypad if we select this,\n'
1100
 
                  'or we use the characters from the keyboard.'),
1101
 
                5, 'geography.use-keypad',
1102
 
                extra_callback=self.update_shortcuts)
1103
 
 
1104
 
        return _('The map'), table
1105
 
 
1106
 
    def set_tilepath(self, *obj):
1107
 
        if self.path_entry.get_text().strip():
1108
 
            config.set('geography.path', self.path_entry.get_text())
1109
 
        else:
1110
 
            config.set('geography.path', GEOGRAPHY_PATH )
1111
 
 
1112
 
    def select_tilepath(self, *obj):
1113
 
        f = Gtk.FileChooserDialog(
1114
 
            _("Select tile cache directory for offline mode"),
1115
 
            action=Gtk.FileChooserAction.SELECT_FOLDER,
1116
 
            buttons=(Gtk.STOCK_CANCEL,
1117
 
                     Gtk.ResponseType.CANCEL,
1118
 
                     Gtk.STOCK_APPLY,
1119
 
                     Gtk.ResponseType.OK))
1120
 
        mpath = config.get('geography.path')
1121
 
        if not mpath:
1122
 
            mpath = HOME_DIR
1123
 
        f.set_current_folder(os.path.dirname(mpath))
1124
 
 
1125
 
        status = f.run()
1126
 
        if status == Gtk.ResponseType.OK:
1127
 
            val = conv_to_unicode(f.get_filename())
1128
 
            if val:
1129
 
                self.path_entry.set_text(val)
1130
 
        f.destroy()
1131