def _is_longitude_field(self, value, context): """Ensure this field can be used for longitudes""" # We can't use datastore_search for this, as we need operators db = _get_engine() metadata = MetaData() table = Table(context['resource'].id, metadata, Column(value, Numeric)) query = select([func.count(1).label('count')], from_obj=table) query = query.where(not_(table.c[value] == None)) query = query.where(or_(cast(table.c[value], Numeric) < -180, cast(table.c[value], Numeric) > 180)) with db.begin() as connection: try: query_result = connection.execute(query) except DataError as e: raise p.toolkit.Invalid(_('Longitude field must contain numeric data')) row = query_result.fetchone() query_result.close() if row['count'] > 0: raise p.toolkit.Invalid(_('Longitude field must be between -180 and 180')) return value
def map_info(self): """Controller action that returns metadata about a given map. As a side effect this will set the content type to application/json @return: A JSON encoded string representing the metadata """ # Specific parameters fetch_id = request.params.get('fetch_id') tile_url_base = 'http://{host}:{port}/database/{database}/table/{table}'.format( host=config['tiledmap.windshaft.host'], port=config['tiledmap.windshaft.port'], database=_get_engine().url.database, table=self.resource_id ) ## Ensure we have at least one map style if not self.view['enable_plot_map'] and not self.view['enable_grid_map'] and not self.view['enable_heat_map']: return json.dumps({ 'geospatial': False, 'fetch_id': fetch_id }) # Prepare result quick_info_template_name = "{base}.{format}.mustache".format( base=self.quick_info_template, format=str(self.resource['format']).lower() ) if not find_template(quick_info_template_name): quick_info_template_name = self.quick_info_template + '.mustache' info_template_name = "{base}.{format}.mustache".format( base=self.info_template, format=str(self.resource['format']).lower() ) if not find_template(info_template_name): info_template_name = self.info_template + '.mustache' quick_info_template = toolkit.render(quick_info_template_name, { 'title': self.info_title, 'fields': self.info_fields }) info_template = toolkit.render(info_template_name, { 'title': self.info_title, 'fields': self.info_fields, 'overlapping_records_view': self.view['overlapping_records_view'] }) result = { 'geospatial': True, 'geom_count': 0, 'total_count': 0, 'bounds': ((83, -170), (-83, 170)), 'zoom_bounds': { 'min': int(config['tiledmap.zoom_bounds.min']), 'max': int(config['tiledmap.zoom_bounds.max']) }, 'initial_zoom': { 'min': int(config['tiledmap.initial_zoom.min']), 'max': int(config['tiledmap.initial_zoom.max']) }, 'tile_layer': { 'url': config['tiledmap.tile_layer.url'], 'opacity': float(config['tiledmap.tile_layer.opacity']) }, 'repeat_map': self.repeat_map, 'map_styles': { }, 'control_options': { 'fullScreen': { 'position': 'topright' }, 'drawShape': { 'draw': { 'polyline': False, 'marker': False, 'circle': False, 'country': True, 'polygon': { 'allowIntersection': False, 'shapeOptions': { 'stroke': True, 'color': '#F44', 'weight': 5, 'opacity': 0.5, 'fill': True, 'fillColor': '#F44', 'fillOpacity': 0.1 } } }, 'position': 'topleft' }, 'selectCountry': { 'draw': { 'fill': '#F44', 'fill-opacity': '0.1', 'stroke': '#F44', 'stroke-opacity': '0.5' } }, 'mapType': { 'position': 'bottomleft' }, 'miniMap': { 'position': 'bottomright', 'tile_layer': { 'url': config['tiledmap.tile_layer.url'] }, 'zoomLevelFixed': 1, #'zoomLevelOffset': -10, 'toggleDisplay': True, 'viewport': { 'marker_zoom': 8, 'rect': { 'weight': 1, 'color': '#00F', 'opacity': 1, 'fill': False }, 'marker': { 'weight': 1, 'color': '#00F', 'opacity': 1, 'radius': 3, 'fillColor': '#00F', 'fillOpacity': 0.2 } } } }, 'plugin_options': { 'tooltipInfo': { 'count_field': '_tiledmap_count', 'template': quick_info_template, }, 'tooltipCount': { 'count_field': '_tiledmap_count' }, 'pointInfo': { 'template': info_template, 'count_field': '_tiledmap_count' } }, 'fetch_id': fetch_id } if self.view['enable_heat_map']: result['map_styles']['heatmap'] = { 'name': _('Heat Map'), 'icon': '<i class="fa fa-fire"></i>', 'controls': ['drawShape', 'mapType', 'fullScreen', 'miniMap'], 'has_grid': False, 'tile_source': { 'url': tile_url_base + '/{z}/{x}/{y}.png', 'params': { 'intensity': config['tiledmap.style.heatmap.intensity'], } }, } result['map_style'] = 'heatmap' if self.view['enable_grid_map']: result['map_styles']['gridded'] = { 'name': _('Grid Map'), 'icon': '<i class="fa fa-th"></i>', 'controls': ['drawShape', 'mapType', 'fullScreen', 'miniMap'], 'plugins': ['tooltipCount'], 'has_grid': self.view['enable_utf_grid'], 'grid_resolution': int(config['tiledmap.style.plot.grid_resolution']), 'tile_source': { 'url': tile_url_base + '/{z}/{x}/{y}.png', 'params': { 'base_color': config['tiledmap.style.gridded.base_color'] } }, 'grid_source': { 'url': tile_url_base + '/{z}/{x}/{y}.grid.json', 'params': { 'interactivity': ','.join(self.query_fields) } } } result['map_style'] = 'gridded' if self.view['enable_plot_map']: result['map_styles']['plot'] = { 'name': _('Plot Map'), 'icon': '<i class="fa fa-dot-circle-o"></i>', 'controls': ['drawShape', 'mapType', 'fullScreen', 'miniMap'], 'plugins': ['tooltipInfo', 'pointInfo'], 'has_grid': self.view['enable_utf_grid'], 'grid_resolution': int(config['tiledmap.style.plot.grid_resolution']), 'tile_source': { 'url': tile_url_base + '/{z}/{x}/{y}.png', 'params': { 'fill_color': config['tiledmap.style.plot.fill_color'], 'line_color': config['tiledmap.style.plot.line_color'] } }, 'grid_source': { 'url': tile_url_base + '/{z}/{x}/{y}.grid.json', 'params': { 'interactivity': ','.join(self.query_fields) } } } result['map_style'] = 'plot' # Get query extent and count info = toolkit.get_action('datastore_query_extent')({}, { 'resource_id': self.resource_id, 'filters': self._get_request_filters(), 'limit': 1, 'q': urllib.unquote(request.params.get('q', '')), 'fields': '_id' }) result['total_count'] = info['total_count'] result['geom_count'] = info['geom_count'] if info['bounds']: result['bounds'] = info['bounds'] response.headers['Content-type'] = 'application/json' return json.dumps(result)