pandamap 1.0.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pandamap might be problematic. Click here for more details.

pandamap-1.0.0/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Pritam Kumar Panda
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.
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: pandamap
3
+ Version: 1.0.0
4
+ Summary: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation
5
+ Author-email: Your Name <your.email@example.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Pritam Kumar Panda
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/yourusername/pandamap
29
+ Project-URL: Bug Tracker, https://github.com/yourusername/pandamap/issues
30
+ Keywords: bioinformatics,protein-ligand,interactions,visualization,drug-design
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
35
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
36
+ Classifier: Topic :: Scientific/Engineering :: Visualization
37
+ Requires-Python: >=3.7
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: numpy>=1.20.0
41
+ Requires-Dist: matplotlib>=3.4.0
42
+ Requires-Dist: biopython>=1.79
43
+ Dynamic: license-file
44
+
45
+ # PandaMap
46
+
47
+ **P**rotein **AND** lig**A**nd interaction **MAP**per: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation
48
+
49
+ ## Overview
50
+
51
+ PandaMap is a lightweight tool for visualizing protein-ligand interactions from PDB files. It generates intuitive 2D interaction diagrams that display both the ligand structure and its interactions with protein residues.
52
+
53
+ Key features:
54
+ - Visualization of protein-ligand interactions with minimal dependencies
55
+ - 2D representation of ligand structure without requiring RDKit
56
+ - Detection of multiple interaction types (hydrogen bonds, π-stacking, hydrophobic)
57
+ - Command-line interface for quick analysis
58
+ - Python API for integration into computational workflows
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install pandamap
64
+ ```
65
+
66
+ ## Dependencies
67
+
68
+ - NumPy
69
+ - Matplotlib
70
+ - BioPython
71
+
72
+ ## Basic Usage
73
+
74
+ ### Command Line Interface
75
+
76
+ ```bash
77
+ # Basic usage
78
+ pandamap protein_ligand.pdb --output interactions.png
79
+
80
+ # Specify a particular ligand by residue name
81
+ pandamap protein_ligand.pdb --ligand LIG
82
+ ```
83
+
84
+ ### Python API
85
+
86
+ ```python
87
+ from pandamap import HybridProtLigMapper
88
+
89
+ # Initialize with PDB file
90
+ mapper = HybridProtLigMapper("protein_ligand.pdb", ligand_resname="LIG")
91
+
92
+ # Run analysis and generate visualization
93
+ output_file = mapper.run_analysis(output_file="interactions.png")
94
+
95
+ # Or run steps separately
96
+ mapper.detect_interactions()
97
+ mapper.estimate_solvent_accessibility()
98
+ mapper.visualize(output_file="interactions.png")
99
+ ```
100
+
101
+ ## Example Output
102
+
103
+ ![PandaMap](test/complex_interactions.png)
104
+
105
+ ## Citation
106
+
107
+ If you use PandaMap in your research, please cite:
108
+
109
+ ```
110
+ Pritam Kumar Panda. (2025). **P**rotein **AND** lig**A**nd interaction **MAP**per: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation. GitHub repository. https://github.com/pritampanda15/PandaMap
111
+ ```
112
+
113
+ ## License
114
+
115
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,71 @@
1
+ # PandaMap
2
+
3
+ **P**rotein **AND** lig**A**nd interaction **MAP**per: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation
4
+
5
+ ## Overview
6
+
7
+ PandaMap is a lightweight tool for visualizing protein-ligand interactions from PDB files. It generates intuitive 2D interaction diagrams that display both the ligand structure and its interactions with protein residues.
8
+
9
+ Key features:
10
+ - Visualization of protein-ligand interactions with minimal dependencies
11
+ - 2D representation of ligand structure without requiring RDKit
12
+ - Detection of multiple interaction types (hydrogen bonds, π-stacking, hydrophobic)
13
+ - Command-line interface for quick analysis
14
+ - Python API for integration into computational workflows
15
+
16
+ ## Installation
17
+
18
+ ```bash
19
+ pip install pandamap
20
+ ```
21
+
22
+ ## Dependencies
23
+
24
+ - NumPy
25
+ - Matplotlib
26
+ - BioPython
27
+
28
+ ## Basic Usage
29
+
30
+ ### Command Line Interface
31
+
32
+ ```bash
33
+ # Basic usage
34
+ pandamap protein_ligand.pdb --output interactions.png
35
+
36
+ # Specify a particular ligand by residue name
37
+ pandamap protein_ligand.pdb --ligand LIG
38
+ ```
39
+
40
+ ### Python API
41
+
42
+ ```python
43
+ from pandamap import HybridProtLigMapper
44
+
45
+ # Initialize with PDB file
46
+ mapper = HybridProtLigMapper("protein_ligand.pdb", ligand_resname="LIG")
47
+
48
+ # Run analysis and generate visualization
49
+ output_file = mapper.run_analysis(output_file="interactions.png")
50
+
51
+ # Or run steps separately
52
+ mapper.detect_interactions()
53
+ mapper.estimate_solvent_accessibility()
54
+ mapper.visualize(output_file="interactions.png")
55
+ ```
56
+
57
+ ## Example Output
58
+
59
+ ![PandaMap](test/complex_interactions.png)
60
+
61
+ ## Citation
62
+
63
+ If you use PandaMap in your research, please cite:
64
+
65
+ ```
66
+ Pritam Kumar Panda. (2025). **P**rotein **AND** lig**A**nd interaction **MAP**per: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation. GitHub repository. https://github.com/pritampanda15/PandaMap
67
+ ```
68
+
69
+ ## License
70
+
71
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,35 @@
1
+ [build-system]
2
+ requires = ["setuptools>=42", "wheel"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "pandamap"
7
+ version = "1.0.0"
8
+ description = "A Python package for visualizing protein-ligand interactions with 2D ligand structure representation"
9
+ readme = "README.md"
10
+ authors = [
11
+ {name = "Your Name", email = "your.email@example.com"}
12
+ ]
13
+ license = {file = "LICENSE"}
14
+ classifiers = [
15
+ "Programming Language :: Python :: 3",
16
+ "License :: OSI Approved :: MIT License",
17
+ "Operating System :: OS Independent",
18
+ "Topic :: Scientific/Engineering :: Bio-Informatics",
19
+ "Topic :: Scientific/Engineering :: Chemistry",
20
+ "Topic :: Scientific/Engineering :: Visualization",
21
+ ]
22
+ keywords = ["bioinformatics", "protein-ligand", "interactions", "visualization", "drug-design"]
23
+ dependencies = [
24
+ "numpy>=1.20.0",
25
+ "matplotlib>=3.4.0",
26
+ "biopython>=1.79",
27
+ ]
28
+ requires-python = ">=3.7"
29
+
30
+ [project.urls]
31
+ "Homepage" = "https://github.com/yourusername/pandamap"
32
+ "Bug Tracker" = "https://github.com/yourusername/pandamap/issues"
33
+
34
+ [project.scripts]
35
+ pandamap = "pandamap.cli:main"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,8 @@
1
+ from setuptools import setup, find_packages
2
+
3
+ # This is a shim for compatibility with older tools
4
+ # pyproject.toml is the preferred way to configure package metadata
5
+ setup(
6
+ package_dir={"": "src"},
7
+ packages=find_packages(where="src"),
8
+ )
@@ -0,0 +1,12 @@
1
+ """
2
+ PandaMap: Protein AND ligAnd interaction MAPper
3
+
4
+ A Python package for visualizing protein-ligand interactions
5
+ with 2D ligand structure representation and minimal external dependencies.
6
+ """
7
+
8
+ __version__ = "0.1.0"
9
+
10
+ from .core import SimpleLigandStructure, HybridProtLigMapper
11
+
12
+ __all__ = ["SimpleLigandStructure", "HybridProtLigMapper"]
@@ -0,0 +1,48 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Command-line interface for PandaMap.
4
+ """
5
+
6
+ import sys
7
+ import argparse
8
+ from pandamap.core import HybridProtLigMapper
9
+
10
+ def main():
11
+ """Command-line interface for PandaMap."""
12
+ parser = argparse.ArgumentParser(
13
+ description='PandaMap: Visualize protein-ligand interactions from PDB files')
14
+
15
+ parser.add_argument('pdb_file', help='Path to PDB file with protein-ligand complex')
16
+ parser.add_argument('--output', '-o', help='Output image file path')
17
+ parser.add_argument('--ligand', '-l', help='Specific ligand residue name to analyze')
18
+ parser.add_argument('--dpi', type=int, default=300, help='Image resolution (default: 300 dpi)')
19
+ parser.add_argument('--title', '-t', help='Custom title for the visualization')
20
+ parser.add_argument('--version', '-v', action='store_true', help='Show version information')
21
+
22
+ args = parser.parse_args()
23
+
24
+ # Handle version flag
25
+ if args.version:
26
+ from pandamap import __version__
27
+ print(f"PandaMap version {__version__}")
28
+ return 0
29
+
30
+ # Check for required pdb_file
31
+ if not hasattr(args, 'pdb_file') or args.pdb_file is None:
32
+ parser.print_help()
33
+ return 1
34
+
35
+ try:
36
+ # Initialize and run the analysis
37
+ mapper = HybridProtLigMapper(args.pdb_file, ligand_resname=args.ligand)
38
+ output_file = mapper.run_analysis(output_file=args.output)
39
+
40
+ print(f"Analysis complete. Visualization saved to: {output_file}")
41
+ return 0
42
+
43
+ except Exception as e:
44
+ print(f"Error: {e}")
45
+ return 1
46
+
47
+ if __name__ == "__main__":
48
+ sys.exit(main())
@@ -0,0 +1,766 @@
1
+ #!/usr/bin/env python
2
+ """
3
+ Core functionality for PandaMap: A Python package for visualizing
4
+ protein-ligand interactions with 2D ligand structure representation.
5
+ """
6
+
7
+ import os
8
+ import math
9
+ from collections import defaultdict
10
+
11
+ import numpy as np
12
+ import matplotlib.pyplot as plt
13
+ from matplotlib.patches import Circle, FancyArrowPatch, Wedge, Rectangle, Polygon
14
+ from matplotlib.lines import Line2D
15
+ import matplotlib.patheffects as path_effects
16
+
17
+ # BioPython imports
18
+ from Bio.PDB import PDBParser, NeighborSearch
19
+
20
+ # Define three_to_one conversion manually if import isn't available
21
+ try:
22
+ from Bio.PDB.Polypeptide import three_to_one
23
+ except ImportError:
24
+ # Define the conversion dictionary manually
25
+ _aa_index = {
26
+ 'ALA': 'A', 'CYS': 'C', 'ASP': 'D', 'GLU': 'E',
27
+ 'PHE': 'F', 'GLY': 'G', 'HIS': 'H', 'ILE': 'I',
28
+ 'LYS': 'K', 'LEU': 'L', 'MET': 'M', 'ASN': 'N',
29
+ 'PRO': 'P', 'GLN': 'Q', 'ARG': 'R', 'SER': 'S',
30
+ 'THR': 'T', 'VAL': 'V', 'TRP': 'W', 'TYR': 'Y'
31
+ }
32
+
33
+ def three_to_one(residue):
34
+ """Convert amino acid three letter code to one letter code."""
35
+ if residue in _aa_index:
36
+ return _aa_index[residue]
37
+ else:
38
+ return "X" # Unknown amino acid
39
+
40
+ class SimpleLigandStructure:
41
+ """
42
+ Class to create a simplified 2D representation of a ligand structure
43
+ without requiring RDKit or other external dependencies.
44
+ """
45
+
46
+ def __init__(self, ligand_atoms):
47
+ """
48
+ Initialize with a list of ligand atoms from a BioPython structure.
49
+
50
+ Parameters:
51
+ -----------
52
+ ligand_atoms : list
53
+ List of BioPython Atom objects from the ligand
54
+ """
55
+ self.ligand_atoms = ligand_atoms
56
+ self.atom_coords = {}
57
+ self.element_colors = {
58
+ 'C': '#808080', # Grey
59
+ 'N': '#0000FF', # Blue
60
+ 'O': '#FF0000', # Red
61
+ 'S': '#FFFF00', # Yellow
62
+ 'P': '#FFA500', # Orange
63
+ 'F': '#00FF00', # Green
64
+ 'Cl': '#00FF00', # Green
65
+ 'Br': '#A52A2A', # Brown
66
+ 'I': '#A020F0', # Purple
67
+ 'H': '#FFFFFF' # White
68
+ }
69
+
70
+ # Record atom coordinates and elements
71
+ for atom in ligand_atoms:
72
+ atom_id = atom.get_id()
73
+ self.atom_coords[atom_id] = {
74
+ 'element': atom.element,
75
+ 'coord': atom.get_coord() # 3D coordinates from PDB
76
+ }
77
+
78
+ def generate_2d_coords(self):
79
+ """
80
+ Generate simplified 2D coordinates for the ligand atoms based on their 3D coordinates.
81
+ This is a very basic projection - in a real application, you would use a proper
82
+ 2D layout algorithm.
83
+
84
+ Returns:
85
+ --------
86
+ dict : Dictionary mapping atom IDs to 2D coordinates
87
+ """
88
+ if not self.atom_coords:
89
+ return {}
90
+
91
+ # Simple projection onto the xy-plane
92
+ coords_2d = {}
93
+
94
+ # Get all 3D coordinates and find center
95
+ all_coords = np.array([info['coord'] for info in self.atom_coords.values()])
96
+ center = np.mean(all_coords, axis=0)
97
+
98
+ # Subtract center to center the molecule
99
+ centered_coords = all_coords - center
100
+
101
+ # Simple PCA-like approach to find main plane
102
+ # (This is a very simplified approach)
103
+ cov_matrix = np.cov(centered_coords.T)
104
+
105
+ try:
106
+ # Get eigenvectors and eigenvalues
107
+ eigenvalues, eigenvectors = np.linalg.eig(cov_matrix)
108
+
109
+ # Sort by eigenvalue in descending order
110
+ idx = eigenvalues.argsort()[::-1]
111
+ eigenvalues = eigenvalues[idx]
112
+ eigenvectors = eigenvectors[:, idx]
113
+
114
+ # Use the first two eigenvectors to define the plane
115
+ plane_vectors = eigenvectors[:, :2]
116
+
117
+ # Project the centered coordinates onto the plane
118
+ projected_coords = np.dot(centered_coords, plane_vectors)
119
+
120
+ # Scale to fit nicely in the visualization
121
+ max_dim = np.max(np.abs(projected_coords))
122
+ scaling_factor = 50.0 / max_dim if max_dim > 0 else 1.0
123
+ projected_coords *= scaling_factor
124
+
125
+ # Store the 2D coordinates
126
+ for i, atom_id in enumerate(self.atom_coords.keys()):
127
+ coords_2d[atom_id] = projected_coords[i]
128
+
129
+ except np.linalg.LinAlgError:
130
+ # Fallback if eigendecomposition fails
131
+ print("Warning: Could not compute optimal projection. Using simple XY projection.")
132
+ for atom_id, info in self.atom_coords.items():
133
+ # Simple scaling of x, y coordinates
134
+ coords_2d[atom_id] = np.array([info['coord'][0], info['coord'][1]]) * 10.0
135
+
136
+ return coords_2d
137
+
138
+ def find_bonds(self, distance_threshold=2.0):
139
+ """
140
+ Find bonds between atoms based on distance.
141
+ This is a simplified approach - in reality, you'd use chemical knowledge.
142
+
143
+ Parameters:
144
+ -----------
145
+ distance_threshold : float
146
+ Maximum distance between atoms to be considered bonded (in Angstroms)
147
+
148
+ Returns:
149
+ --------
150
+ list : List of tuples (atom_id1, atom_id2) representing bonds
151
+ """
152
+ bonds = []
153
+ atom_ids = list(self.atom_coords.keys())
154
+
155
+ for i in range(len(atom_ids)):
156
+ for j in range(i+1, len(atom_ids)):
157
+ atom1_id = atom_ids[i]
158
+ atom2_id = atom_ids[j]
159
+
160
+ coord1 = self.atom_coords[atom1_id]['coord']
161
+ coord2 = self.atom_coords[atom2_id]['coord']
162
+
163
+ # Calculate Euclidean distance
164
+ distance = np.linalg.norm(coord1 - coord2)
165
+
166
+ # If distance is below threshold, consider them bonded
167
+ if distance < distance_threshold:
168
+ bonds.append((atom1_id, atom2_id))
169
+
170
+ return bonds
171
+
172
+ def draw_on_axes(self, ax, center=(0, 0), radius=80):
173
+ """
174
+ Draw a simplified 2D representation of the ligand on the given axes.
175
+
176
+ Parameters:
177
+ -----------
178
+ ax : matplotlib.axes.Axes
179
+ The axes on which to draw
180
+ center : tuple
181
+ The (x, y) coordinates where the center of the molecule should be
182
+ radius : float
183
+ The approximate radius the molecule should occupy
184
+
185
+ Returns:
186
+ --------
187
+ dict : Dictionary mapping atom IDs to their 2D positions in the plot
188
+ """
189
+ # Generate 2D coordinates
190
+ coords_2d = self.generate_2d_coords()
191
+
192
+ if not coords_2d:
193
+ # If we couldn't generate coordinates, draw a simple placeholder
194
+ print("Warning: Could not generate ligand coordinates. Drawing placeholder.")
195
+ circle = Circle(center, radius/2, fill=False, edgecolor='black', linestyle='-')
196
+ ax.add_patch(circle)
197
+ ax.text(center[0], center[1], "Ligand", ha='center', va='center')
198
+ return {}
199
+
200
+ # Find bonds
201
+ bonds = self.find_bonds()
202
+
203
+ # Scale coordinates to fit within the specified radius
204
+ all_coords = np.array(list(coords_2d.values()))
205
+ max_extent = np.max(np.abs(all_coords))
206
+ scaling_factor = radius / (max_extent * 1.2) # Leave some margin
207
+
208
+ # Create a mapping of atom IDs to positions in the plot
209
+ atom_positions = {}
210
+
211
+ # Draw bonds first (so they're below atoms)
212
+ for atom1_id, atom2_id in bonds:
213
+ pos1 = coords_2d[atom1_id] * scaling_factor + center
214
+ pos2 = coords_2d[atom2_id] * scaling_factor + center
215
+
216
+ # Draw bond as a line
217
+ line = Line2D([pos1[0], pos2[0]], [pos1[1], pos2[1]],
218
+ color='black', linewidth=1.5, zorder=2)
219
+ ax.add_line(line)
220
+
221
+ # Draw atoms as circles
222
+ for atom_id, coord in coords_2d.items():
223
+ # Scale and shift the position
224
+ pos = coord * scaling_factor + center
225
+ atom_positions[atom_id] = pos
226
+
227
+ element = self.atom_coords[atom_id]['element']
228
+ color = self.element_colors.get(element, 'gray')
229
+
230
+ # Determine size based on element (larger for heavier atoms)
231
+ size = 8 if element in ['C', 'H'] else 10
232
+
233
+ # Draw atom
234
+ circle = Circle(pos, size, facecolor=color, edgecolor='black',
235
+ linewidth=1, alpha=0.8, zorder=3)
236
+ ax.add_patch(circle)
237
+
238
+ # Add element label (except for carbon)
239
+ if element != 'C':
240
+ ax.text(pos[0], pos[1], element, ha='center', va='center',
241
+ fontsize=8, fontweight='bold', color='white', zorder=4)
242
+
243
+ return atom_positions
244
+
245
+
246
+ class HybridProtLigMapper:
247
+ """
248
+ Class for analyzing protein-ligand interactions and creating
249
+ visualizations with a simplified ligand structure.
250
+ """
251
+
252
+ def __init__(self, pdb_file, ligand_resname=None):
253
+ """
254
+ Initialize with a PDB file containing a protein-ligand complex.
255
+
256
+ Parameters:
257
+ -----------
258
+ pdb_file : str
259
+ Path to the PDB file with protein and ligand
260
+ ligand_resname : str, optional
261
+ Specific residue name of the ligand to focus on
262
+ """
263
+ self.pdb_file = pdb_file
264
+ self.ligand_resname = ligand_resname
265
+
266
+ # Parse the PDB file
267
+ self.parser = PDBParser(QUIET=True)
268
+ self.structure = self.parser.get_structure('complex', pdb_file)
269
+ self.model = self.structure[0]
270
+
271
+ # Separate ligand from protein
272
+ self.protein_atoms = []
273
+ self.ligand_atoms = []
274
+ self.protein_residues = {}
275
+ self.ligand_residue = None
276
+
277
+ for residue in self.model.get_residues():
278
+ # Store ligand atoms (HETATM records)
279
+ if residue.id[0] != ' ': # Non-standard residue (HETATM)
280
+ if ligand_resname is None or residue.resname == ligand_resname:
281
+ for atom in residue:
282
+ self.ligand_atoms.append(atom)
283
+ if self.ligand_residue is None:
284
+ self.ligand_residue = residue
285
+ else: # Standard residues (protein)
286
+ res_id = (residue.resname, residue.id[1])
287
+ self.protein_residues[res_id] = residue
288
+ for atom in residue:
289
+ self.protein_atoms.append(atom)
290
+
291
+ # Check if we found a ligand
292
+ if not self.ligand_atoms:
293
+ raise ValueError("No ligand (HETATM) found in the PDB file.")
294
+
295
+ # Storage for the interaction data
296
+ self.interactions = {
297
+ 'hydrogen_bonds': [],
298
+ 'carbon_pi': [],
299
+ 'pi_pi_stacking': [],
300
+ 'donor_pi': [],
301
+ 'amide_pi': [],
302
+ 'hydrophobic': []
303
+ }
304
+
305
+ # Will store residues that interact with the ligand
306
+ self.interacting_residues = set()
307
+
308
+ # For solvent accessibility information (simplified)
309
+ self.solvent_accessible = set()
310
+
311
+ # Create the simple ligand structure
312
+ self.ligand_structure = SimpleLigandStructure(self.ligand_atoms)
313
+
314
+ def detect_interactions(self,
315
+ h_bond_cutoff=3.5,
316
+ pi_stack_cutoff=5.5,
317
+ hydrophobic_cutoff=4.0):
318
+ """
319
+ Detect all interactions between protein and ligand.
320
+
321
+ Parameters:
322
+ -----------
323
+ h_bond_cutoff : float
324
+ Distance cutoff for hydrogen bonds in Angstroms
325
+ pi_stack_cutoff : float
326
+ Distance cutoff for pi-stacking interactions in Angstroms
327
+ hydrophobic_cutoff : float
328
+ Distance cutoff for hydrophobic interactions in Angstroms
329
+ """
330
+ # Use neighbor search for efficiency
331
+ ns = NeighborSearch(self.protein_atoms)
332
+ max_cutoff = max(h_bond_cutoff, pi_stack_cutoff, hydrophobic_cutoff)
333
+
334
+ # Define amino acid categories
335
+ aromatic_residues = {'PHE', 'TYR', 'TRP', 'HIS'}
336
+ h_bond_donors = {'ARG', 'LYS', 'HIS', 'ASN', 'GLN', 'SER', 'THR', 'TYR', 'TRP'}
337
+ h_bond_acceptors = {'ASP', 'GLU', 'ASN', 'GLN', 'HIS', 'SER', 'THR', 'TYR'}
338
+ neg_charged = {'ASP', 'GLU'}
339
+ amide_residues = {'ASN', 'GLN'}
340
+ hydrophobic_residues = {'ALA', 'VAL', 'LEU', 'ILE', 'MET', 'PHE', 'TRP', 'PRO', 'TYR'}
341
+
342
+ # Check each ligand atom for interactions
343
+ for lig_atom in self.ligand_atoms:
344
+ # Find protein atoms within cutoff distance
345
+ nearby_atoms = ns.search(lig_atom.get_coord(), max_cutoff)
346
+
347
+ for prot_atom in nearby_atoms:
348
+ prot_res = prot_atom.get_parent()
349
+ distance = lig_atom - prot_atom
350
+
351
+ # Store interacting residue for later visualization
352
+ res_id = (prot_res.resname, prot_res.id[1])
353
+ self.interacting_residues.add(res_id)
354
+
355
+ # Determine interaction types based on distance and atom/residue types
356
+
357
+ # 1. Hydrogen bonds - N and O atoms within cutoff
358
+ if distance <= h_bond_cutoff:
359
+ if lig_atom.element in ['N', 'O'] and prot_atom.element in ['N', 'O']:
360
+ self.interactions['hydrogen_bonds'].append({
361
+ 'ligand_atom': lig_atom,
362
+ 'protein_atom': prot_atom,
363
+ 'protein_residue': prot_res,
364
+ 'distance': distance
365
+ })
366
+
367
+ # 2. Pi-stacking interactions - aromatic residues
368
+ if distance <= pi_stack_cutoff and prot_res.resname in aromatic_residues:
369
+ if lig_atom.element == 'C' and prot_atom.element == 'C':
370
+ self.interactions['pi_pi_stacking'].append({
371
+ 'ligand_atom': lig_atom,
372
+ 'protein_atom': prot_atom,
373
+ 'protein_residue': prot_res,
374
+ 'distance': distance
375
+ })
376
+
377
+ # 3. Carbon-Pi interactions
378
+ if distance <= pi_stack_cutoff and prot_res.resname in aromatic_residues:
379
+ if lig_atom.element == 'C':
380
+ self.interactions['carbon_pi'].append({
381
+ 'ligand_atom': lig_atom,
382
+ 'protein_atom': prot_atom,
383
+ 'protein_residue': prot_res,
384
+ 'distance': distance
385
+ })
386
+
387
+ # 4. Donor-Pi interactions - negatively charged residues
388
+ if distance <= pi_stack_cutoff and prot_res.resname in neg_charged:
389
+ if lig_atom.element == 'C':
390
+ self.interactions['donor_pi'].append({
391
+ 'ligand_atom': lig_atom,
392
+ 'protein_atom': prot_atom,
393
+ 'protein_residue': prot_res,
394
+ 'distance': distance
395
+ })
396
+
397
+ # 5. Amide-Pi interactions
398
+ if distance <= pi_stack_cutoff and prot_res.resname in amide_residues:
399
+ if lig_atom.element == 'C':
400
+ self.interactions['amide_pi'].append({
401
+ 'ligand_atom': lig_atom,
402
+ 'protein_atom': prot_atom,
403
+ 'protein_residue': prot_res,
404
+ 'distance': distance
405
+ })
406
+
407
+ # 6. Hydrophobic interactions
408
+ if distance <= hydrophobic_cutoff:
409
+ if (prot_res.resname in hydrophobic_residues and
410
+ lig_atom.element == 'C' and prot_atom.element == 'C'):
411
+ self.interactions['hydrophobic'].append({
412
+ 'ligand_atom': lig_atom,
413
+ 'protein_atom': prot_atom,
414
+ 'protein_residue': prot_res,
415
+ 'distance': distance
416
+ })
417
+
418
+ # Deduplicate interactions by residue for cleaner visualization
419
+ # Keep only one interaction of each type per residue
420
+ for interaction_type in self.interactions:
421
+ by_residue = defaultdict(list)
422
+ for interaction in self.interactions[interaction_type]:
423
+ res_id = (interaction['protein_residue'].resname,
424
+ interaction['protein_residue'].id[1])
425
+ by_residue[res_id].append(interaction)
426
+
427
+ # Keep only the closest interaction for each residue and type
428
+ closest_interactions = []
429
+ for res_id, res_interactions in by_residue.items():
430
+ closest = min(res_interactions, key=lambda x: x['distance'])
431
+ closest_interactions.append(closest)
432
+
433
+ self.interactions[interaction_type] = closest_interactions
434
+
435
+ def estimate_solvent_accessibility(self):
436
+ """
437
+ Estimate which residues might be solvent accessible.
438
+ This is a simplified approach since we're trying to match the example image.
439
+ In a real implementation, you'd use DSSP or a similar tool.
440
+ """
441
+ # For simplicity, mark all residues as solvent accessible
442
+ # In a real implementation, you'd use a proper algorithm
443
+ self.solvent_accessible = self.interacting_residues.copy()
444
+
445
+ def visualize(self, output_file='protein_ligand_interactions.png',
446
+ figsize=(12, 12), dpi=300, title=None):
447
+ """
448
+ Generate a 2D visualization of protein-ligand interactions
449
+ matching the style of the reference image.
450
+
451
+ Parameters:
452
+ -----------
453
+ output_file : str
454
+ Path where the output image will be saved
455
+ figsize : tuple
456
+ Figure size in inches (width, height)
457
+ dpi : int
458
+ Resolution in dots per inch
459
+ title : str, optional
460
+ Title for the plot
461
+ """
462
+ # Create figure
463
+ fig, ax = plt.subplots(figsize=figsize)
464
+
465
+ # Add light blue background for ligand (matching reference)
466
+ ligand_radius = 90
467
+ ligand_pos = (0, 0)
468
+ ligand_circle = Circle(ligand_pos, ligand_radius, facecolor='#ADD8E6',
469
+ edgecolor='none', alpha=0.4, zorder=1)
470
+ ax.add_patch(ligand_circle)
471
+
472
+ # Draw the simplified ligand structure
473
+ atom_positions = self.ligand_structure.draw_on_axes(ax, center=ligand_pos, radius=ligand_radius*0.8)
474
+
475
+ # Place interacting residues in a circle around the ligand
476
+ n_residues = len(self.interacting_residues)
477
+ if n_residues == 0:
478
+ print("Warning: No interacting residues detected.")
479
+ n_residues = 1 # Avoid division by zero
480
+
481
+ # Calculate positions for residues
482
+ radius = 250 # Distance from center to residues
483
+ residue_positions = {}
484
+
485
+ # Arrange residues in a circle
486
+ for i, res_id in enumerate(sorted(self.interacting_residues)):
487
+ angle = 2 * math.pi * i / n_residues
488
+ x = radius * math.cos(angle)
489
+ y = radius * math.sin(angle)
490
+ residue_positions[res_id] = (x, y)
491
+
492
+ # Draw solvent accessibility highlight (light blue circle behind residue)
493
+ if res_id in self.solvent_accessible:
494
+ solvent_circle = Circle((x, y), 40, facecolor='#ADD8E6',
495
+ edgecolor='none', alpha=0.3, zorder=1)
496
+ ax.add_patch(solvent_circle)
497
+
498
+ # Draw residue node as rectangle with rounded corners like in reference image
499
+ # For simplicity, we'll use a white rectangle with black border
500
+ rect_width = 60
501
+ rect_height = 30
502
+ residue_box = Rectangle((x-rect_width/2, y-rect_height/2), rect_width, rect_height,
503
+ facecolor='white', edgecolor='black', linewidth=1.5,
504
+ zorder=2, alpha=1.0)
505
+ ax.add_patch(residue_box)
506
+
507
+ # Add residue label (NAME NUMBER) like in reference image
508
+ resname, resnum = res_id
509
+ label = f"{resname} {resnum}"
510
+ text = ax.text(x, y, label, ha='center', va='center',
511
+ fontsize=11, fontweight='bold', zorder=3)
512
+ text.set_path_effects([path_effects.withStroke(linewidth=2,
513
+ foreground='white')])
514
+
515
+ # Define interaction styles to match the reference image
516
+ interaction_styles = {
517
+ 'hydrogen_bonds': {
518
+ 'color': 'green',
519
+ 'linestyle': '-',
520
+ 'linewidth': 1.5,
521
+ 'marker_text': 'H',
522
+ 'marker_color': 'green',
523
+ 'marker_bg': '#E0FFE0', # Light green bg
524
+ 'name': 'Hydrogen Bond'
525
+ },
526
+ 'carbon_pi': {
527
+ 'color': '#666666', # Dark gray
528
+ 'linestyle': '--',
529
+ 'linewidth': 1.5,
530
+ 'marker_text': 'C',
531
+ 'marker_color': '#666666',
532
+ 'marker_bg': 'white',
533
+ 'name': 'Carbon-Pi interaction'
534
+ },
535
+ 'pi_pi_stacking': {
536
+ 'color': '#9370DB', # Medium purple
537
+ 'linestyle': '--',
538
+ 'linewidth': 1.5,
539
+ 'marker_text': 'π',
540
+ 'marker_color': '#9370DB',
541
+ 'marker_bg': 'white',
542
+ 'name': 'Pi-Pi stacking'
543
+ },
544
+ 'donor_pi': {
545
+ 'color': '#FF69B4', # Hot pink
546
+ 'linestyle': '--',
547
+ 'linewidth': 1.5,
548
+ 'marker_text': 'D',
549
+ 'marker_color': '#FF69B4',
550
+ 'marker_bg': 'white',
551
+ 'name': 'Donor-Pi interaction'
552
+ },
553
+ 'amide_pi': {
554
+ 'color': '#A52A2A', # Brown
555
+ 'linestyle': '--',
556
+ 'linewidth': 1.5,
557
+ 'marker_text': 'A',
558
+ 'marker_color': '#A52A2A',
559
+ 'marker_bg': 'white',
560
+ 'name': 'Amide-Pi interaction'
561
+ },
562
+ 'hydrophobic': {
563
+ 'color': '#808080', # Gray
564
+ 'linestyle': ':',
565
+ 'linewidth': 1.0,
566
+ 'marker_text': 'h',
567
+ 'marker_color': '#808080',
568
+ 'marker_bg': 'white',
569
+ 'name': 'Hydrophobic'
570
+ }
571
+ }
572
+
573
+ # Draw interaction lines for each residue
574
+ for interaction_type, interactions in self.interactions.items():
575
+ if interaction_type not in interaction_styles:
576
+ continue
577
+
578
+ style = interaction_styles[interaction_type]
579
+
580
+ for interaction in interactions:
581
+ res = interaction['protein_residue']
582
+ res_id = (res.resname, res.id[1])
583
+ lig_atom = interaction['ligand_atom']
584
+
585
+ if res_id in residue_positions:
586
+ res_pos = residue_positions[res_id]
587
+
588
+ # Try to use actual ligand atom position if available
589
+ if lig_atom.get_id() in atom_positions:
590
+ lig_pos = atom_positions[lig_atom.get_id()]
591
+ else:
592
+ # Determine the point on ligand circle edge as fallback
593
+ # Get the angle from center to residue
594
+ dx = res_pos[0] - ligand_pos[0]
595
+ dy = res_pos[1] - ligand_pos[1]
596
+ angle = math.atan2(dy, dx)
597
+
598
+ # Calculate point on ligand circle edge
599
+ lig_edge_x = ligand_pos[0] + ligand_radius * math.cos(angle)
600
+ lig_edge_y = ligand_pos[1] + ligand_radius * math.sin(angle)
601
+ lig_pos = (lig_edge_x, lig_edge_y)
602
+
603
+ # Draw curved line from ligand to residue
604
+ line = FancyArrowPatch(
605
+ lig_pos, res_pos,
606
+ connectionstyle="arc3,rad=0.1",
607
+ color=style['color'],
608
+ linestyle=style['linestyle'],
609
+ linewidth=style['linewidth'],
610
+ arrowstyle='-',
611
+ alpha=0.7,
612
+ zorder=4
613
+ )
614
+ ax.add_patch(line)
615
+
616
+ # Add interaction marker (circle with letter)
617
+ midpoint_x = (lig_pos[0] + res_pos[0]) / 2
618
+ midpoint_y = (lig_pos[1] + res_pos[1]) / 2
619
+
620
+ # Draw hexagonal marker like in reference image
621
+ n_sides = 6 if 'pi' in interaction_type else 0 # Hexagon for pi interactions
622
+ marker_radius = 12
623
+
624
+ if n_sides > 0:
625
+ # Draw a hexagon for pi interactions
626
+ angles = np.linspace(0, 2*np.pi, n_sides+1)[:-1]
627
+ hex_vertices = np.array([
628
+ [midpoint_x + marker_radius * np.cos(angle),
629
+ midpoint_y + marker_radius * np.sin(angle)]
630
+ for angle in angles
631
+ ])
632
+ hex_patch = Polygon(
633
+ hex_vertices,
634
+ closed=True,
635
+ facecolor=style.get('marker_bg', 'white'),
636
+ edgecolor=style['marker_color'],
637
+ linewidth=1.5,
638
+ alpha=0.9,
639
+ zorder=5
640
+ )
641
+ ax.add_patch(hex_patch)
642
+ else:
643
+ # Draw circle for other interactions
644
+ marker_circle = Circle(
645
+ (midpoint_x, midpoint_y), marker_radius,
646
+ facecolor=style.get('marker_bg', 'white'),
647
+ edgecolor=style['marker_color'],
648
+ linewidth=1.5,
649
+ alpha=0.9,
650
+ zorder=5
651
+ )
652
+ ax.add_patch(marker_circle)
653
+
654
+ # Add interaction symbol
655
+ ax.text(
656
+ midpoint_x, midpoint_y,
657
+ style['marker_text'],
658
+ ha='center', va='center',
659
+ fontsize=9, color=style['marker_color'],
660
+ fontweight='bold',
661
+ zorder=6
662
+ )
663
+
664
+ # Add legend box with title matching reference image
665
+ legend_title = "Interacting structural groups"
666
+ legend_elements = []
667
+
668
+ # Add a "RES 1" element for residue representation
669
+ residue_patch = Rectangle((0, 0), 1, 1, facecolor='white',
670
+ edgecolor='black', label='Interacting structural groups')
671
+ legend_elements.append(residue_patch)
672
+
673
+ # Interaction type markers for legend
674
+ for int_type, style in interaction_styles.items():
675
+ # Only include interaction types that are present
676
+ if self.interactions[int_type]:
677
+ if int_type == 'hydrogen_bonds':
678
+ # Special handling for H-bonds to match reference
679
+ line = Line2D([0], [0], color=style['color'],
680
+ linestyle=style['linestyle'], linewidth=style['linewidth'],
681
+ marker='o', markerfacecolor=style.get('marker_bg', 'white'),
682
+ markeredgecolor=style['color'],
683
+ markersize=8, label=style['name'])
684
+ elif 'pi' in int_type:
685
+ # Hexagonal markers for pi interactions
686
+ line = Line2D([0], [0], color=style['color'],
687
+ linestyle=style['linestyle'], linewidth=style['linewidth'],
688
+ marker='h', markerfacecolor=style.get('marker_bg', 'white'),
689
+ markeredgecolor=style['color'],
690
+ markersize=8, label=style['name'])
691
+ else:
692
+ # Circular markers for other interactions
693
+ line = Line2D([0], [0], color=style['color'],
694
+ linestyle=style['linestyle'], linewidth=style['linewidth'],
695
+ marker='o', markerfacecolor=style.get('marker_bg', 'white'),
696
+ markeredgecolor=style['color'],
697
+ markersize=8, label=style['name'])
698
+ legend_elements.append(line)
699
+
700
+ # Add solvent accessibility indicator
701
+ if self.solvent_accessible:
702
+ solvent_patch = Rectangle((0, 0), 1, 1, facecolor='#ADD8E6',
703
+ alpha=0.3, edgecolor=None, label='Solvent accessible')
704
+ legend_elements.append(solvent_patch)
705
+
706
+ # Create legend box in top right corner like reference image
707
+ legend = ax.legend(
708
+ handles=legend_elements,
709
+ title=legend_title,
710
+ loc='upper right',
711
+ frameon=True,
712
+ framealpha=0.7,
713
+ fontsize=9,
714
+ title_fontsize=10
715
+ )
716
+
717
+ # Set plot limits and appearance
718
+ max_coord = radius + 100
719
+ ax.set_xlim(-max_coord, max_coord)
720
+ ax.set_ylim(-max_coord, max_coord)
721
+ ax.set_aspect('equal')
722
+ ax.axis('off')
723
+
724
+ # Add title
725
+ if title:
726
+ plt.title(title, fontsize=16)
727
+ else:
728
+ plt.title(f"Protein-Ligand Interactions: {os.path.basename(self.pdb_file)}",
729
+ fontsize=16)
730
+
731
+ # Save figure
732
+ plt.tight_layout()
733
+ plt.savefig(output_file, dpi=dpi, bbox_inches='tight')
734
+ plt.close()
735
+
736
+ print(f"Interaction diagram saved to {output_file}")
737
+ return output_file
738
+
739
+ def run_analysis(self, output_file=None):
740
+ """
741
+ Run the complete analysis pipeline.
742
+
743
+ Parameters:
744
+ -----------
745
+ output_file : str, optional
746
+ Path where the output image will be saved. If None, a default name will be generated.
747
+
748
+ Returns:
749
+ --------
750
+ str : Path to the generated visualization file
751
+ """
752
+ if output_file is None:
753
+ base_name = os.path.splitext(os.path.basename(self.pdb_file))[0]
754
+ output_file = f"{base_name}_interactions.png"
755
+
756
+ # Detect protein-ligand interactions
757
+ print("Detecting interactions...")
758
+ self.detect_interactions()
759
+
760
+ # Estimate solvent accessibility
761
+ print("Estimating solvent accessibility...")
762
+ self.estimate_solvent_accessibility()
763
+
764
+ # Generate visualization
765
+ print("Generating visualization...")
766
+ return self.visualize(output_file=output_file)
@@ -0,0 +1,115 @@
1
+ Metadata-Version: 2.4
2
+ Name: pandamap
3
+ Version: 1.0.0
4
+ Summary: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation
5
+ Author-email: Your Name <your.email@example.com>
6
+ License: MIT License
7
+
8
+ Copyright (c) 2025 Pritam Kumar Panda
9
+
10
+ Permission is hereby granted, free of charge, to any person obtaining a copy
11
+ of this software and associated documentation files (the "Software"), to deal
12
+ in the Software without restriction, including without limitation the rights
13
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
14
+ copies of the Software, and to permit persons to whom the Software is
15
+ furnished to do so, subject to the following conditions:
16
+
17
+ The above copyright notice and this permission notice shall be included in all
18
+ copies or substantial portions of the Software.
19
+
20
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26
+ SOFTWARE.
27
+
28
+ Project-URL: Homepage, https://github.com/yourusername/pandamap
29
+ Project-URL: Bug Tracker, https://github.com/yourusername/pandamap/issues
30
+ Keywords: bioinformatics,protein-ligand,interactions,visualization,drug-design
31
+ Classifier: Programming Language :: Python :: 3
32
+ Classifier: License :: OSI Approved :: MIT License
33
+ Classifier: Operating System :: OS Independent
34
+ Classifier: Topic :: Scientific/Engineering :: Bio-Informatics
35
+ Classifier: Topic :: Scientific/Engineering :: Chemistry
36
+ Classifier: Topic :: Scientific/Engineering :: Visualization
37
+ Requires-Python: >=3.7
38
+ Description-Content-Type: text/markdown
39
+ License-File: LICENSE
40
+ Requires-Dist: numpy>=1.20.0
41
+ Requires-Dist: matplotlib>=3.4.0
42
+ Requires-Dist: biopython>=1.79
43
+ Dynamic: license-file
44
+
45
+ # PandaMap
46
+
47
+ **P**rotein **AND** lig**A**nd interaction **MAP**per: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation
48
+
49
+ ## Overview
50
+
51
+ PandaMap is a lightweight tool for visualizing protein-ligand interactions from PDB files. It generates intuitive 2D interaction diagrams that display both the ligand structure and its interactions with protein residues.
52
+
53
+ Key features:
54
+ - Visualization of protein-ligand interactions with minimal dependencies
55
+ - 2D representation of ligand structure without requiring RDKit
56
+ - Detection of multiple interaction types (hydrogen bonds, π-stacking, hydrophobic)
57
+ - Command-line interface for quick analysis
58
+ - Python API for integration into computational workflows
59
+
60
+ ## Installation
61
+
62
+ ```bash
63
+ pip install pandamap
64
+ ```
65
+
66
+ ## Dependencies
67
+
68
+ - NumPy
69
+ - Matplotlib
70
+ - BioPython
71
+
72
+ ## Basic Usage
73
+
74
+ ### Command Line Interface
75
+
76
+ ```bash
77
+ # Basic usage
78
+ pandamap protein_ligand.pdb --output interactions.png
79
+
80
+ # Specify a particular ligand by residue name
81
+ pandamap protein_ligand.pdb --ligand LIG
82
+ ```
83
+
84
+ ### Python API
85
+
86
+ ```python
87
+ from pandamap import HybridProtLigMapper
88
+
89
+ # Initialize with PDB file
90
+ mapper = HybridProtLigMapper("protein_ligand.pdb", ligand_resname="LIG")
91
+
92
+ # Run analysis and generate visualization
93
+ output_file = mapper.run_analysis(output_file="interactions.png")
94
+
95
+ # Or run steps separately
96
+ mapper.detect_interactions()
97
+ mapper.estimate_solvent_accessibility()
98
+ mapper.visualize(output_file="interactions.png")
99
+ ```
100
+
101
+ ## Example Output
102
+
103
+ ![PandaMap](test/complex_interactions.png)
104
+
105
+ ## Citation
106
+
107
+ If you use PandaMap in your research, please cite:
108
+
109
+ ```
110
+ Pritam Kumar Panda. (2025). **P**rotein **AND** lig**A**nd interaction **MAP**per: A Python package for visualizing protein-ligand interactions with 2D ligand structure representation. GitHub repository. https://github.com/pritampanda15/PandaMap
111
+ ```
112
+
113
+ ## License
114
+
115
+ This project is licensed under the MIT License - see the LICENSE file for details.
@@ -0,0 +1,13 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ setup.py
5
+ src/pandamap/__init__.py
6
+ src/pandamap/cli.py
7
+ src/pandamap/core.py
8
+ src/pandamap.egg-info/PKG-INFO
9
+ src/pandamap.egg-info/SOURCES.txt
10
+ src/pandamap.egg-info/dependency_links.txt
11
+ src/pandamap.egg-info/entry_points.txt
12
+ src/pandamap.egg-info/requires.txt
13
+ src/pandamap.egg-info/top_level.txt
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ pandamap = pandamap.cli:main
@@ -0,0 +1,3 @@
1
+ numpy>=1.20.0
2
+ matplotlib>=3.4.0
3
+ biopython>=1.79
@@ -0,0 +1 @@
1
+ pandamap