• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1# Copyright 2020 The gRPC Authors
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#     http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""The Python AsyncIO implementation of the gRPC route guide server."""
15
16import asyncio
17import logging
18import math
19import time
20from typing import AsyncIterable, Iterable
21
22import grpc
23import route_guide_pb2
24import route_guide_pb2_grpc
25import route_guide_resources
26
27
28def get_feature(
29    feature_db: Iterable[route_guide_pb2.Feature], point: route_guide_pb2.Point
30) -> route_guide_pb2.Feature:
31    """Returns Feature at given location or None."""
32    for feature in feature_db:
33        if feature.location == point:
34            return feature
35    return None
36
37
38def get_distance(
39    start: route_guide_pb2.Point, end: route_guide_pb2.Point
40) -> float:
41    """Distance between two points."""
42    coord_factor = 10000000.0
43    lat_1 = start.latitude / coord_factor
44    lat_2 = end.latitude / coord_factor
45    lon_1 = start.longitude / coord_factor
46    lon_2 = end.longitude / coord_factor
47    lat_rad_1 = math.radians(lat_1)
48    lat_rad_2 = math.radians(lat_2)
49    delta_lat_rad = math.radians(lat_2 - lat_1)
50    delta_lon_rad = math.radians(lon_2 - lon_1)
51
52    # Formula is based on http://mathforum.org/library/drmath/view/51879.html
53    a = pow(math.sin(delta_lat_rad / 2), 2) + (
54        math.cos(lat_rad_1)
55        * math.cos(lat_rad_2)
56        * pow(math.sin(delta_lon_rad / 2), 2)
57    )
58    c = 2 * math.atan2(math.sqrt(a), math.sqrt(1 - a))
59    R = 6371000
60    # metres
61    return R * c
62
63
64class RouteGuideServicer(route_guide_pb2_grpc.RouteGuideServicer):
65    """Provides methods that implement functionality of route guide server."""
66
67    def __init__(self) -> None:
68        self.db = route_guide_resources.read_route_guide_database()
69
70    def GetFeature(
71        self, request: route_guide_pb2.Point, unused_context
72    ) -> route_guide_pb2.Feature:
73        feature = get_feature(self.db, request)
74        if feature is None:
75            return route_guide_pb2.Feature(name="", location=request)
76        else:
77            return feature
78
79    async def ListFeatures(
80        self, request: route_guide_pb2.Rectangle, unused_context
81    ) -> AsyncIterable[route_guide_pb2.Feature]:
82        left = min(request.lo.longitude, request.hi.longitude)
83        right = max(request.lo.longitude, request.hi.longitude)
84        top = max(request.lo.latitude, request.hi.latitude)
85        bottom = min(request.lo.latitude, request.hi.latitude)
86        for feature in self.db:
87            if (
88                feature.location.longitude >= left
89                and feature.location.longitude <= right
90                and feature.location.latitude >= bottom
91                and feature.location.latitude <= top
92            ):
93                yield feature
94
95    async def RecordRoute(
96        self,
97        request_iterator: AsyncIterable[route_guide_pb2.Point],
98        unused_context,
99    ) -> route_guide_pb2.RouteSummary:
100        point_count = 0
101        feature_count = 0
102        distance = 0.0
103        prev_point = None
104
105        start_time = time.time()
106        async for point in request_iterator:
107            point_count += 1
108            if get_feature(self.db, point):
109                feature_count += 1
110            if prev_point:
111                distance += get_distance(prev_point, point)
112            prev_point = point
113
114        elapsed_time = time.time() - start_time
115        return route_guide_pb2.RouteSummary(
116            point_count=point_count,
117            feature_count=feature_count,
118            distance=int(distance),
119            elapsed_time=int(elapsed_time),
120        )
121
122    async def RouteChat(
123        self,
124        request_iterator: AsyncIterable[route_guide_pb2.RouteNote],
125        unused_context,
126    ) -> AsyncIterable[route_guide_pb2.RouteNote]:
127        prev_notes = []
128        async for new_note in request_iterator:
129            for prev_note in prev_notes:
130                if prev_note.location == new_note.location:
131                    yield prev_note
132            prev_notes.append(new_note)
133
134
135async def serve() -> None:
136    server = grpc.aio.server()
137    route_guide_pb2_grpc.add_RouteGuideServicer_to_server(
138        RouteGuideServicer(), server
139    )
140    server.add_insecure_port("[::]:50051")
141    await server.start()
142    await server.wait_for_termination()
143
144
145if __name__ == "__main__":
146    logging.basicConfig(level=logging.INFO)
147    asyncio.get_event_loop().run_until_complete(serve())
148