├── .gitignore
├── LICENSE
├── README.md
└── merge_map
├── config
└── merge_map.rviz
├── launch
└── merge_map_launch.py
├── merge_map
├── __init__.py
└── merge_map.py
├── package.xml
├── resource
└── merge_map
├── setup.cfg
├── setup.py
└── test
├── test_copyright.py
├── test_flake8.py
└── test_pep257.py
/.gitignore:
--------------------------------------------------------------------------------
1 | # Byte-compiled / optimized / DLL files
2 | __pycache__/
3 | *.py[cod]
4 | *$py.class
5 |
6 | # C extensions
7 | *.so
8 |
9 | # Distribution / packaging
10 | .Python
11 | build/
12 | develop-eggs/
13 | dist/
14 | downloads/
15 | eggs/
16 | .eggs/
17 | lib/
18 | lib64/
19 | parts/
20 | sdist/
21 | var/
22 | wheels/
23 | pip-wheel-metadata/
24 | share/python-wheels/
25 | *.egg-info/
26 | .installed.cfg
27 | *.egg
28 | MANIFEST
29 |
30 | # PyInstaller
31 | # Usually these files are written by a python script from a template
32 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
33 | *.manifest
34 | *.spec
35 |
36 | # Installer logs
37 | pip-log.txt
38 | pip-delete-this-directory.txt
39 |
40 | # Unit test / coverage reports
41 | htmlcov/
42 | .tox/
43 | .nox/
44 | .coverage
45 | .coverage.*
46 | .cache
47 | nosetests.xml
48 | coverage.xml
49 | *.cover
50 | *.py,cover
51 | .hypothesis/
52 | .pytest_cache/
53 |
54 | # Translations
55 | *.mo
56 | *.pot
57 |
58 | # Django stuff:
59 | *.log
60 | local_settings.py
61 | db.sqlite3
62 | db.sqlite3-journal
63 |
64 | # Flask stuff:
65 | instance/
66 | .webassets-cache
67 |
68 | # Scrapy stuff:
69 | .scrapy
70 |
71 | # Sphinx documentation
72 | docs/_build/
73 |
74 | # PyBuilder
75 | target/
76 |
77 | # Jupyter Notebook
78 | .ipynb_checkpoints
79 |
80 | # IPython
81 | profile_default/
82 | ipython_config.py
83 |
84 | # pyenv
85 | .python-version
86 |
87 | # pipenv
88 | # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
89 | # However, in case of collaboration, if having platform-specific dependencies or dependencies
90 | # having no cross-platform support, pipenv may install dependencies that don't work, or not
91 | # install all needed dependencies.
92 | #Pipfile.lock
93 |
94 | # PEP 582; used by e.g. github.com/David-OConnor/pyflow
95 | __pypackages__/
96 |
97 | # Celery stuff
98 | celerybeat-schedule
99 | celerybeat.pid
100 |
101 | # SageMath parsed files
102 | *.sage.py
103 |
104 | # Environments
105 | .env
106 | .venv
107 | env/
108 | venv/
109 | ENV/
110 | env.bak/
111 | venv.bak/
112 |
113 | # Spyder project settings
114 | .spyderproject
115 | .spyproject
116 |
117 | # Rope project settings
118 | .ropeproject
119 |
120 | # mkdocs documentation
121 | /site
122 |
123 | # mypy
124 | .mypy_cache/
125 | .dmypy.json
126 | dmypy.json
127 |
128 | # Pyre type checker
129 | .pyre/
130 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2023 Abdulkadir TÜRE
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # mapMergeForMultiRobotMapping-ROS2
2 | This ROS2 node subscribes to two different map messages, merges them, and
3 | publishes the merged map to the /merge_map topic.
4 |
5 | ## How does it work
6 |
7 | The Merge Map ROS2 package is designed to merge map messages from `/map1` and `/map2` topics, and publish the merged map to the `/merge_map` topic. To use this package, follow the instructions below:
8 |
9 | 1. Copy the `merge_map` folder to the `src` directory of your ROS2 workspace.
10 | 2. Run the following command: `ros2 launch merge_map merge_map_launch.py` This command starts the map merging node and opens an RViz2 window.
11 | 3. In the RViz2 window, you can observe the merged map. Whenever one of the maps being merged is updated, the `merge_map` node will also update the merged map accordingly.
12 |
13 | Make sure to have ROS2 installed and properly configured in your environment before using this package. For more information, please refer to the ROS2 documentation.
14 |
15 |
16 | ## launch.py customization
17 |
18 | The Merge Map ROS2 package can be customized and launched in your own `launch.py` file by adding the following code block. You can update `/yourMapName1` and `/yourMapName2` topics according to your project:
19 |
20 | ```
21 | Node(
22 | package='merge_map',
23 | executable='merge_map',
24 | output='screen',
25 | parameters=[{'use_sim_time': True}],
26 | remappings=[
27 | ("/map1", "/yourMapName1"),
28 | ("/map2", "/yourMapName2"),
29 | ],
30 | ),
31 | ```
32 |
33 | ## Example
34 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/merge_map/config/merge_map.rviz:
--------------------------------------------------------------------------------
1 | Panels:
2 | - Class: rviz_common/Displays
3 | Help Height: 70
4 | Name: Displays
5 | Property Tree Widget:
6 | Expanded:
7 | - /Global Options1
8 | Splitter Ratio: 0.5
9 | Tree Height: 70
10 | - Class: rviz_common/Selection
11 | Name: Selection
12 | - Class: rviz_common/Tool Properties
13 | Expanded:
14 | - /2D Goal Pose1
15 | - /Publish Point1
16 | Name: Tool Properties
17 | Splitter Ratio: 0.5886790156364441
18 | - Class: rviz_common/Views
19 | Expanded:
20 | - /Current View1
21 | Name: Views
22 | Splitter Ratio: 0.5
23 | - Class: rviz_common/Time
24 | Experimental: false
25 | Name: Time
26 | SyncMode: 0
27 | SyncSource: ""
28 | Visualization Manager:
29 | Class: ""
30 | Displays:
31 | - Alpha: 0.5
32 | Cell Size: 1
33 | Class: rviz_default_plugins/Grid
34 | Color: 160; 160; 164
35 | Enabled: true
36 | Line Style:
37 | Line Width: 0.029999999329447746
38 | Value: Lines
39 | Name: Grid
40 | Normal Cell Count: 0
41 | Offset:
42 | X: 0
43 | Y: 0
44 | Z: 0
45 | Plane: XY
46 | Plane Cell Count: 10
47 | Reference Frame:
48 | Value: true
49 | - Alpha: 0.699999988079071
50 | Class: rviz_default_plugins/Map
51 | Color Scheme: map
52 | Draw Behind: false
53 | Enabled: true
54 | Name: Map
55 | Topic:
56 | Depth: 5
57 | Durability Policy: Volatile
58 | Filter size: 10
59 | History Policy: Keep Last
60 | Reliability Policy: Reliable
61 | Value: /merge_map
62 | Update Topic:
63 | Depth: 5
64 | Durability Policy: Volatile
65 | History Policy: Keep Last
66 | Reliability Policy: Reliable
67 | Value: /merge_map_updates
68 | Use Timestamp: false
69 | Value: true
70 | - Alpha: 1
71 | Buffer Length: 1
72 | Class: rviz_default_plugins/Path
73 | Color: 246; 211; 45
74 | Enabled: true
75 | Head Diameter: 0.30000001192092896
76 | Head Length: 0.20000000298023224
77 | Length: 0.30000001192092896
78 | Line Style: Billboards
79 | Line Width: 0.10000000149011612
80 | Name: Path
81 | Offset:
82 | X: 0
83 | Y: 0
84 | Z: 0
85 | Pose Color: 255; 85; 255
86 | Pose Style: None
87 | Radius: 0.029999999329447746
88 | Shaft Diameter: 0.10000000149011612
89 | Shaft Length: 0.10000000149011612
90 | Topic:
91 | Depth: 5
92 | Durability Policy: Volatile
93 | Filter size: 10
94 | History Policy: Keep Last
95 | Reliability Policy: Reliable
96 | Value: /tb3_1/visual_path
97 | Value: true
98 | - Alpha: 1
99 | Buffer Length: 1
100 | Class: rviz_default_plugins/Path
101 | Color: 25; 255; 0
102 | Enabled: true
103 | Head Diameter: 0.30000001192092896
104 | Head Length: 0.20000000298023224
105 | Length: 0.30000001192092896
106 | Line Style: Billboards
107 | Line Width: 0.10000000149011612
108 | Name: Path
109 | Offset:
110 | X: 0
111 | Y: 0
112 | Z: 0
113 | Pose Color: 255; 85; 255
114 | Pose Style: None
115 | Radius: 0.029999999329447746
116 | Shaft Diameter: 0.10000000149011612
117 | Shaft Length: 0.10000000149011612
118 | Topic:
119 | Depth: 5
120 | Durability Policy: Volatile
121 | Filter size: 10
122 | History Policy: Keep Last
123 | Reliability Policy: Reliable
124 | Value: /tb3_0/visual_path
125 | Value: true
126 | - Class: rviz_default_plugins/Image
127 | Enabled: true
128 | Max Value: 1
129 | Median window: 5
130 | Min Value: 0
131 | Name: Image
132 | Normalize Range: true
133 | Topic:
134 | Depth: 5
135 | Durability Policy: Volatile
136 | History Policy: Keep Last
137 | Reliability Policy: Reliable
138 | Value: /drone/bottom/image_raw
139 | Value: true
140 | Enabled: true
141 | Global Options:
142 | Background Color: 48; 48; 48
143 | Fixed Frame: merge_map
144 | Frame Rate: 30
145 | Name: root
146 | Tools:
147 | - Class: rviz_default_plugins/Interact
148 | Hide Inactive Objects: true
149 | - Class: rviz_default_plugins/MoveCamera
150 | - Class: rviz_default_plugins/Select
151 | - Class: rviz_default_plugins/FocusCamera
152 | - Class: rviz_default_plugins/Measure
153 | Line color: 128; 128; 0
154 | - Class: rviz_default_plugins/SetInitialPose
155 | Covariance x: 0.25
156 | Covariance y: 0.25
157 | Covariance yaw: 0.06853891909122467
158 | Topic:
159 | Depth: 5
160 | Durability Policy: Volatile
161 | History Policy: Keep Last
162 | Reliability Policy: Reliable
163 | Value: /initialpose
164 | - Class: rviz_default_plugins/SetGoal
165 | Topic:
166 | Depth: 5
167 | Durability Policy: Volatile
168 | History Policy: Keep Last
169 | Reliability Policy: Reliable
170 | Value: /goal_pose
171 | - Class: rviz_default_plugins/PublishPoint
172 | Single click: true
173 | Topic:
174 | Depth: 5
175 | Durability Policy: Volatile
176 | History Policy: Keep Last
177 | Reliability Policy: Reliable
178 | Value: /clicked_point
179 | Transformation:
180 | Current:
181 | Class: rviz_default_plugins/TF
182 | Value: true
183 | Views:
184 | Current:
185 | Class: rviz_default_plugins/Orbit
186 | Distance: 23.42510223388672
187 | Enable Stereo Rendering:
188 | Stereo Eye Separation: 0.05999999865889549
189 | Stereo Focal Distance: 1
190 | Swap Stereo Eyes: false
191 | Value: false
192 | Focal Point:
193 | X: 0.16153769195079803
194 | Y: 2.438749074935913
195 | Z: 0.00016973871970549226
196 | Focal Shape Fixed Size: true
197 | Focal Shape Size: 0.05000000074505806
198 | Invert Z Axis: false
199 | Name: Current View
200 | Near Clip Distance: 0.009999999776482582
201 | Pitch: 1.5697963237762451
202 | Target Frame:
203 | Value: Orbit (rviz)
204 | Yaw: 3.1404001712799072
205 | Saved: ~
206 | Window Geometry:
207 | Displays:
208 | collapsed: false
209 | Height: 604
210 | Hide Left Dock: false
211 | Hide Right Dock: true
212 | Image:
213 | collapsed: false
214 | QMainWindow State: 000000ff00000000fd0000000400000000000001e5000001befc0200000009fb0000001200530065006c0065006300740069006f006e00000001e10000009b0000005c00fffffffb0000001e0054006f006f006c002000500072006f007000650072007400690065007302000001ed000001df00000185000000a3fb000000120056006900650077007300200054006f006f02000001df000002110000018500000122fb000000200054006f006f006c002000500072006f0070006500720074006900650073003203000002880000011d000002210000017afb000000100044006900730070006c006100790073000000003d000000c9000000c900fffffffb0000002000730065006c0065006300740069006f006e00200062007500660066006500720200000138000000aa0000023a00000294fb00000014005700690064006500530074006500720065006f02000000e6000000d2000003ee0000030bfb0000000c004b0069006e0065006300740200000186000001060000030c00000261fb0000000a0049006d006100670065010000003d000001be0000002800ffffff000000010000010f00000168fc0200000003fb0000001e0054006f006f006c002000500072006f00700065007200740069006500730100000041000000780000000000000000fb0000000a00560069006500770073000000003d00000168000000a400fffffffb0000001200530065006c0065006300740069006f006e010000025a000000b200000000000000000000000200000490000000a9fc0100000001fb0000000a00560069006500770073030000004e00000080000002e10000019700000003000002fb0000003efc0100000002fb0000000800540069006d00650100000000000002fb000002fb00fffffffb0000000800540069006d0065010000000000000450000000000000000000000110000001be00000004000000040000000800000008fc0000000100000002000000010000000a0054006f006f006c00730100000000ffffffff0000000000000000
215 | Selection:
216 | collapsed: false
217 | Time:
218 | collapsed: false
219 | Tool Properties:
220 | collapsed: false
221 | Views:
222 | collapsed: true
223 | Width: 763
224 | X: 603
225 | Y: 127
226 |
--------------------------------------------------------------------------------
/merge_map/launch/merge_map_launch.py:
--------------------------------------------------------------------------------
1 | # Authors: Abdulkadir Ture
2 | # Github : abdulkadrtr
3 |
4 | import os
5 |
6 | from ament_index_python.packages import get_package_share_directory
7 | from launch import LaunchDescription
8 | from launch.actions import IncludeLaunchDescription
9 | from launch.launch_description_sources import PythonLaunchDescriptionSource
10 | from launch.substitutions import LaunchConfiguration
11 | from launch_ros.actions import Node
12 |
13 | def generate_launch_description():
14 | rviz_file = os.path.join(get_package_share_directory('merge_map'), 'config', 'merge_map.rviz')
15 | return LaunchDescription([
16 | Node(
17 | package='rviz2',
18 | executable='rviz2',
19 | name='rviz2',
20 | output='screen',
21 | arguments=['-d', rviz_file],
22 | parameters=[{'use_sim_time': True}]
23 | ),
24 | Node(
25 | package='merge_map',
26 | executable='merge_map',
27 | output='screen',
28 | parameters=[{'use_sim_time': True}]
29 | ),
30 | ])
31 |
--------------------------------------------------------------------------------
/merge_map/merge_map/__init__.py:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdulkadrtr/mapMergeForMultiRobotMapping-ROS2/321c0bd3182fc00e725569e2b4e07721f5e4d4cd/merge_map/merge_map/__init__.py
--------------------------------------------------------------------------------
/merge_map/merge_map/merge_map.py:
--------------------------------------------------------------------------------
1 | # Authors: Abdulkadir Ture
2 | # Github : abdulkadrtr
3 |
4 | import rclpy
5 | from rclpy.node import Node
6 | from nav_msgs.msg import OccupancyGrid
7 | import numpy as np
8 |
9 | def merge_maps(map1, map2):
10 | merged_map = OccupancyGrid()
11 | merged_map.header = map1.header
12 | merged_map.header.frame_id = 'merge_map'
13 | min_x = min(map1.info.origin.position.x, map2.info.origin.position.x)
14 | min_y = min(map1.info.origin.position.y, map2.info.origin.position.y)
15 | max_x = max(map1.info.origin.position.x + (map1.info.width * map1.info.resolution),
16 | map2.info.origin.position.x + (map2.info.width * map2.info.resolution))
17 | max_y = max(map1.info.origin.position.y + (map1.info.height * map1.info.resolution),
18 | map2.info.origin.position.y + (map2.info.height * map2.info.resolution))
19 | merged_map.info.origin.position.x = min_x
20 | merged_map.info.origin.position.y = min_y
21 | merged_map.info.resolution = min(map1.info.resolution, map2.info.resolution)
22 | merged_map.info.width = int(np.ceil((max_x - min_x) / merged_map.info.resolution))
23 | merged_map.info.height = int(np.ceil((max_y - min_y) / merged_map.info.resolution))
24 | merged_map.data = [-1] * (merged_map.info.width * merged_map.info.height)
25 | for y in range(map1.info.height):
26 | for x in range(map1.info.width):
27 | i = x + y * map1.info.width
28 | merged_x = int(np.floor((map1.info.origin.position.x + x * map1.info.resolution - min_x) / merged_map.info.resolution))
29 | merged_y = int(np.floor((map1.info.origin.position.y + y * map1.info.resolution - min_y) / merged_map.info.resolution))
30 | merged_i = merged_x + merged_y * merged_map.info.width
31 | merged_map.data[merged_i] = map1.data[i]
32 | for y in range(map2.info.height):
33 | for x in range(map2.info.width):
34 | i = x + y * map2.info.width
35 | merged_x = int(np.floor((map2.info.origin.position.x + x * map2.info.resolution - min_x) / merged_map.info.resolution))
36 | merged_y = int(np.floor((map2.info.origin.position.y + y * map2.info.resolution - min_y) / merged_map.info.resolution))
37 | merged_i = merged_x + merged_y * merged_map.info.width
38 | if merged_map.data[merged_i] == -1:
39 | merged_map.data[merged_i] = map2.data[i]
40 | return merged_map
41 |
42 | class MergeMapNode(Node):
43 | def __init__(self):
44 | super().__init__('merge_map_node')
45 | self.publisher = self.create_publisher(OccupancyGrid, '/merge_map', 10)
46 | self.subscription = self.create_subscription(OccupancyGrid, '/map1', self.map1_callback, 10)
47 | self.subscription = self.create_subscription(OccupancyGrid, '/map2', self.map2_callback, 10)
48 | self.map1 = None
49 | self.map2 = None
50 |
51 | def map1_callback(self, msg):
52 | self.map1 = msg
53 | if self.map2 is not None:
54 | msg = merge_maps(self.map1, self.map2)
55 | self.publisher.publish(msg)
56 |
57 | def map2_callback(self, msg):
58 | self.map2 = msg
59 | if self.map1 is not None:
60 | msg = merge_maps(self.map1, self.map2)
61 | self.publisher.publish(msg)
62 |
63 | def main(args=None):
64 | rclpy.init(args=args)
65 | merge_map_node = MergeMapNode()
66 | rclpy.spin(merge_map_node)
67 | merge_map_node.destroy_node()
68 | rclpy.shutdown()
69 |
70 | if __name__ == '__main__':
71 | main()
72 |
--------------------------------------------------------------------------------
/merge_map/package.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | merge_map
5 | 0.0.0
6 | TODO: Package description
7 | abd
8 | TODO: License declaration
9 |
10 | ament_copyright
11 | ament_flake8
12 | ament_pep257
13 | python3-pytest
14 |
15 |
16 | ament_python
17 | rclpy
18 | nav_msgs
19 | geometry_msgs
20 | sensor_msgs
21 |
22 |
23 |
24 |
--------------------------------------------------------------------------------
/merge_map/resource/merge_map:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/abdulkadrtr/mapMergeForMultiRobotMapping-ROS2/321c0bd3182fc00e725569e2b4e07721f5e4d4cd/merge_map/resource/merge_map
--------------------------------------------------------------------------------
/merge_map/setup.cfg:
--------------------------------------------------------------------------------
1 | [develop]
2 | script_dir=$base/lib/merge_map
3 | [install]
4 | install_scripts=$base/lib/merge_map
5 |
--------------------------------------------------------------------------------
/merge_map/setup.py:
--------------------------------------------------------------------------------
1 | import os
2 | from glob import glob
3 | from setuptools import setup
4 |
5 | package_name = 'merge_map'
6 |
7 | setup(
8 | name=package_name,
9 | version='0.0.0',
10 | packages=[package_name],
11 | data_files=[
12 | ('share/ament_index/resource_index/packages',
13 | ['resource/' + package_name]),
14 | ('share/' + package_name, ['package.xml']),
15 | (os.path.join('share', package_name, 'launch'), glob('launch/*.py')),
16 | (os.path.join('share', package_name, 'config'), glob('config/*')),
17 | ],
18 | install_requires=['setuptools'],
19 | zip_safe=True,
20 | maintainer='abdulkadrtr',
21 | maintainer_email='abdulkadirthe@gmail.com',
22 | description='ROS2 Merge_Map package',
23 | license='TODO: License declaration',
24 | tests_require=['pytest'],
25 | entry_points={
26 | 'console_scripts': [
27 | 'merge_map = merge_map.merge_map:main'
28 | ],
29 | },
30 | )
31 |
--------------------------------------------------------------------------------
/merge_map/test/test_copyright.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 Open Source Robotics Foundation, Inc.
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 |
15 | from ament_copyright.main import main
16 | import pytest
17 |
18 |
19 | # Remove the `skip` decorator once the source file(s) have a copyright header
20 | @pytest.mark.skip(reason='No copyright header has been placed in the generated source file.')
21 | @pytest.mark.copyright
22 | @pytest.mark.linter
23 | def test_copyright():
24 | rc = main(argv=['.', 'test'])
25 | assert rc == 0, 'Found errors'
26 |
--------------------------------------------------------------------------------
/merge_map/test/test_flake8.py:
--------------------------------------------------------------------------------
1 | # Copyright 2017 Open Source Robotics Foundation, Inc.
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 |
15 | from ament_flake8.main import main_with_errors
16 | import pytest
17 |
18 |
19 | @pytest.mark.flake8
20 | @pytest.mark.linter
21 | def test_flake8():
22 | rc, errors = main_with_errors(argv=[])
23 | assert rc == 0, \
24 | 'Found %d code style errors / warnings:\n' % len(errors) + \
25 | '\n'.join(errors)
26 |
--------------------------------------------------------------------------------
/merge_map/test/test_pep257.py:
--------------------------------------------------------------------------------
1 | # Copyright 2015 Open Source Robotics Foundation, Inc.
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 |
15 | from ament_pep257.main import main
16 | import pytest
17 |
18 |
19 | @pytest.mark.linter
20 | @pytest.mark.pep257
21 | def test_pep257():
22 | rc = main(argv=['.', 'test'])
23 | assert rc == 0, 'Found code style errors / warnings'
24 |
--------------------------------------------------------------------------------