gomyck-tools 1.3.1__py3-none-any.whl → 1.3.3__py3-none-any.whl

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.
Files changed (66) hide show
  1. ctools/__init__.py +0 -0
  2. ctools/aes_tools.py +35 -0
  3. ctools/api_result.py +55 -0
  4. ctools/application.py +386 -0
  5. ctools/b64.py +7 -0
  6. ctools/bashPath.py +13 -0
  7. ctools/bottle_web_base.py +169 -0
  8. ctools/bottle_webserver.py +143 -0
  9. ctools/bottle_websocket.py +75 -0
  10. ctools/browser_element_tools.py +314 -0
  11. ctools/call.py +71 -0
  12. ctools/cdebug.py +143 -0
  13. ctools/cftp.py +74 -0
  14. ctools/cjson.py +54 -0
  15. ctools/ckafka.py +159 -0
  16. ctools/compile_tools.py +18 -0
  17. ctools/console.py +55 -0
  18. ctools/coord_trans.py +127 -0
  19. ctools/credis.py +111 -0
  20. ctools/cron_lite.py +245 -0
  21. ctools/ctoken.py +34 -0
  22. ctools/cword.py +30 -0
  23. ctools/czip.py +130 -0
  24. ctools/database.py +185 -0
  25. ctools/date_utils.py +43 -0
  26. ctools/dict_wrapper.py +20 -0
  27. ctools/douglas_rarefy.py +136 -0
  28. ctools/download_tools.py +57 -0
  29. ctools/enums.py +4 -0
  30. ctools/ex.py +31 -0
  31. ctools/excelOpt.py +36 -0
  32. ctools/html_soup.py +35 -0
  33. ctools/http_utils.py +24 -0
  34. ctools/images_tools.py +27 -0
  35. ctools/imgDialog.py +44 -0
  36. ctools/metrics.py +131 -0
  37. ctools/mqtt_utils.py +289 -0
  38. ctools/obj.py +20 -0
  39. ctools/pacth.py +74 -0
  40. ctools/plan_area_tools.py +97 -0
  41. ctools/process_pool.py +36 -0
  42. ctools/pty_tools.py +72 -0
  43. ctools/resource_bundle_tools.py +121 -0
  44. ctools/rsa.py +70 -0
  45. ctools/screenshot_tools.py +127 -0
  46. ctools/sign.py +20 -0
  47. ctools/sm_tools.py +49 -0
  48. ctools/snow_id.py +76 -0
  49. ctools/str_diff.py +20 -0
  50. ctools/string_tools.py +85 -0
  51. ctools/sys_info.py +157 -0
  52. ctools/sys_log.py +89 -0
  53. ctools/thread_pool.py +35 -0
  54. ctools/upload_tools.py +40 -0
  55. ctools/win_canvas.py +83 -0
  56. ctools/win_control.py +106 -0
  57. ctools/word_fill.py +562 -0
  58. ctools/word_fill_entity.py +46 -0
  59. ctools/work_path.py +69 -0
  60. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.3.dist-info}/METADATA +4 -2
  61. gomyck_tools-1.3.3.dist-info/RECORD +64 -0
  62. {gomyck_tools-1.3.1.dist-info → gomyck_tools-1.3.3.dist-info}/WHEEL +1 -1
  63. gomyck_tools-1.3.3.dist-info/licenses/LICENSE +13 -0
  64. gomyck_tools-1.3.3.dist-info/top_level.txt +1 -0
  65. gomyck_tools-1.3.1.dist-info/RECORD +0 -4
  66. gomyck_tools-1.3.1.dist-info/top_level.txt +0 -1
ctools/cdebug.py ADDED
@@ -0,0 +1,143 @@
1
+ import subprocess
2
+ import sys
3
+ import threading
4
+ from datetime import datetime
5
+ from queue import Queue, Empty
6
+
7
+
8
+ class ProgramInterceptor:
9
+ def __init__(self, command):
10
+ self.command = command
11
+ timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
12
+ self.log_filename = f"program_io_log_{timestamp}.txt"
13
+ self.process = None
14
+ self.log_queue = Queue()
15
+
16
+ def start(self):
17
+ # 启动子进程
18
+ self.process = subprocess.Popen(
19
+ self.command,
20
+ stdin=subprocess.PIPE,
21
+ stdout=subprocess.PIPE,
22
+ stderr=subprocess.PIPE,
23
+ bufsize=0,
24
+ universal_newlines=True
25
+ )
26
+
27
+ # 启动日志写入线程
28
+ log_thread = threading.Thread(target=self._write_log_thread, daemon=True)
29
+ log_thread.start()
30
+
31
+ # 记录初始信息
32
+ self._enqueue_log("header", f"Command: {' '.join(self.command)}")
33
+ self._enqueue_log("header", f"Start time: {datetime.now()}")
34
+ self._enqueue_log("header", "-" * 50)
35
+
36
+ # 启动输出转发线程
37
+ stdout_thread = threading.Thread(
38
+ target=self._forward_stream,
39
+ args=(self.process.stdout, sys.stdout, "stdout"),
40
+ daemon=True
41
+ )
42
+ stderr_thread = threading.Thread(
43
+ target=self._forward_stream,
44
+ args=(self.process.stderr, sys.stderr, "stderr"),
45
+ daemon=True
46
+ )
47
+
48
+ stdout_thread.start()
49
+ stderr_thread.start()
50
+
51
+ # 主线程处理输入转发
52
+ try:
53
+ while True:
54
+ if self.process.poll() is not None:
55
+ break
56
+
57
+ # 读取用户输入
58
+ try:
59
+ user_input = sys.stdin.readline()
60
+ if not user_input: # EOF
61
+ break
62
+
63
+ # 记录输入
64
+ self._enqueue_log("stdin", user_input)
65
+
66
+ # 转发到子进程
67
+ try:
68
+ self.process.stdin.write(user_input)
69
+ self.process.stdin.flush()
70
+ except (BrokenPipeError, OSError):
71
+ break
72
+
73
+ except KeyboardInterrupt:
74
+ break
75
+
76
+ finally:
77
+ # 清理工作
78
+ self.process.terminate()
79
+
80
+ # 等待所有输出处理完成
81
+ stdout_thread.join(timeout=1)
82
+ stderr_thread.join(timeout=1)
83
+
84
+ # 记录结束信息
85
+ self._enqueue_log("header", "-" * 50)
86
+ self._enqueue_log("header", f"End time: {datetime.now()}")
87
+ self._enqueue_log("header", f"Exit code: {self.process.returncode}")
88
+
89
+ # 等待日志写入完成
90
+ self.log_queue.put(None) # 结束信号
91
+ log_thread.join(timeout=2)
92
+
93
+ def _forward_stream(self, source, target, stream_name):
94
+ """转发数据流并记录"""
95
+ for line in iter(source.readline, ''):
96
+ # 转发到目标
97
+ target.write(line)
98
+ target.flush()
99
+
100
+ # 记录输出
101
+ self._enqueue_log(stream_name, line)
102
+
103
+ def _enqueue_log(self, stream_type, content):
104
+ """将日志内容放入队列"""
105
+ self.log_queue.put((stream_type, content))
106
+
107
+ def _write_log_thread(self):
108
+ """日志写入线程"""
109
+ with open(self.log_filename, 'w', encoding='utf-8') as log_file:
110
+ while True:
111
+ try:
112
+ item = self.log_queue.get(timeout=0.5)
113
+ if item is None: # 结束信号
114
+ break
115
+
116
+ stream_type, content = item
117
+
118
+ if stream_type == 'stderr': continue
119
+
120
+ if stream_type == "header":
121
+ log_file.write(content + "\n")
122
+ else:
123
+ direction = ">>>" if stream_type == "stdin" else "<<<"
124
+ log_file.write(f"{direction} {stream_type}: {content}")
125
+
126
+ log_file.flush()
127
+
128
+ except Empty:
129
+ if self.process.poll() is not None:
130
+ continue
131
+
132
+
133
+ def main():
134
+ if len(sys.argv) < 2:
135
+ print("Usage: cdebug.py <program> [args...]")
136
+ print("Example: cdebug.py python script.py arg1 arg2")
137
+ sys.exit(1)
138
+ interceptor = ProgramInterceptor(sys.argv[1:])
139
+ interceptor.start()
140
+
141
+
142
+ if __name__ == "__main__":
143
+ main()
ctools/cftp.py ADDED
@@ -0,0 +1,74 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2025/1/24 08:53'
5
+
6
+ import io
7
+ import time
8
+ from ftplib import FTP, error_perm, error_temp
9
+
10
+ class CFTP:
11
+ """
12
+ with open('xx/xx.md', 'rb') as file:
13
+ ftp_host = 'x.x.x.x'
14
+ ftp_username = 'x'
15
+ ftp_password = 'x'
16
+ CFTP(ftp_host, ftp_username, ftp_password).upload_file_to_ftp('xx.md', file)
17
+ """
18
+ def __init__(self, host, username, password, timeout=30, max_retries=3, retry_delay=5):
19
+ self.host = host
20
+ self.username = username
21
+ self.password = password
22
+ self.ftp:FTP = None
23
+ self.timeout = timeout
24
+ self.max_retries = max_retries
25
+ self.retry_delay = retry_delay
26
+
27
+ def upload_file_to_ftp(self, file_name, file:io.BytesIO, ftp_directory='/'):
28
+ if not file_name: raise Exception('文件名不能为空')
29
+ if not file: raise Exception('文件不能为空')
30
+ for attempt in range(self.max_retries):
31
+ try:
32
+ if not self.ftp:
33
+ ftp = FTP(self.host, timeout=self.timeout)
34
+ ftp.login(self.username, self.password)
35
+ ftp.set_pasv(True)
36
+ self.ftp = ftp
37
+ try:
38
+ self.ftp.cwd(ftp_directory)
39
+ except error_perm:
40
+ print(f"FTP 目录不存在:{ftp_directory}")
41
+ self.ftp.mkd(ftp_directory)
42
+ print(f"FTP 目录创建成功:{ftp_directory}, 正在切换到目录:{ftp_directory}")
43
+ self.ftp.cwd(ftp_directory)
44
+ print(f"正在上传文件:{file_name}")
45
+ self.ftp.storbinary(f"STOR {file_name}", file)
46
+ self.ftp.quit()
47
+ print(f"文件成功上传到 FTP: {file_name}")
48
+ return
49
+ except (error_perm, error_temp) as e:
50
+ try:
51
+ self.ftp.quit()
52
+ except Exception:
53
+ pass
54
+ self.ftp = None
55
+ print(f"FTP 错误:{e}")
56
+ if attempt < self.max_retries - 1:
57
+ print(f"正在尝试重连... 第 {attempt + 1} 次重试...")
58
+ time.sleep(self.retry_delay)
59
+ else:
60
+ print("重试次数已用尽,上传失败。")
61
+ raise
62
+ except Exception as e:
63
+ try:
64
+ self.ftp.quit()
65
+ except Exception:
66
+ pass
67
+ self.ftp = None
68
+ print(f"连接或上传出现异常:{e}")
69
+ if attempt < self.max_retries - 1:
70
+ print(f"正在尝试重连... 第 {attempt + 1} 次重试...")
71
+ time.sleep(self.retry_delay)
72
+ else:
73
+ print("重试次数已用尽,上传失败。")
74
+ raise
ctools/cjson.py ADDED
@@ -0,0 +1,54 @@
1
+ import jsonpickle
2
+
3
+ # 需要转换成str的属性
4
+ str_value_keys = []
5
+ jsonpickle.set_preferred_backend('json')
6
+ jsonpickle.set_encoder_options('json', ensure_ascii=False)
7
+ jsonpickle.set_decoder_options('json')
8
+
9
+ def dumps(obj, **kwargs) -> str:
10
+ """
11
+ 将对象转换为json字符串
12
+ :param obj: 对象
13
+ :return: json 字符串
14
+ """
15
+ # indent = 2 可以美化输出
16
+ if obj is None: return None
17
+ if type(obj) == str: return obj
18
+ return f'{jsonpickle.encode(obj, unpicklable=False, make_refs=False, **kwargs)}'
19
+
20
+ def loads(json_str: str, **kwargs) -> dict:
21
+ """
22
+ 将json字符串转换为对象
23
+ :param json_str: json 字符串
24
+ :return: 对象
25
+ """
26
+ return jsonpickle.decode(json_str, **kwargs)
27
+
28
+ def unify_to_str(json_str: str) -> str:
29
+ if not str_value_keys and len(str_value_keys) == 0: return json_str
30
+ obj = loads(json_str)
31
+ if isinstance(obj, list):
32
+ _handle_list(obj)
33
+ elif isinstance(obj, dict):
34
+ _handle_dict(obj)
35
+ return dumps(obj)
36
+
37
+ def _handle_list(data):
38
+ for o in data:
39
+ if isinstance(o, list):
40
+ _handle_list(o)
41
+ elif isinstance(o, dict):
42
+ _handle_dict(o)
43
+
44
+ def _handle_dict(data):
45
+ for k, v in data.items():
46
+ if isinstance(v, list):
47
+ _handle_list(v)
48
+ elif isinstance(v, dict):
49
+ _handle_dict(v)
50
+ elif k in str_value_keys:
51
+ try:
52
+ data[k] = str(v)
53
+ except Exception:
54
+ pass
ctools/ckafka.py ADDED
@@ -0,0 +1,159 @@
1
+ #!/usr/bin/env python
2
+ # -*- coding: UTF-8 -*-
3
+ __author__ = 'haoyang'
4
+ __date__ = '2024/9/5 10:39'
5
+
6
+ import time
7
+ from threading import Thread, Lock
8
+
9
+ from kafka import KafkaProducer, errors, KafkaConsumer
10
+ from kafka.producer.future import FutureRecordMetadata
11
+
12
+ from ctools import thread_pool
13
+ from ctools.cjson import dumps
14
+
15
+ """
16
+ import time
17
+ from datetime import datetime
18
+
19
+ from ctools import thread_pool, string_tools
20
+ from ctools.ckafka import CKafka
21
+
22
+ c = CKafka(kafka_url='192.168.3.160:9094', secure=True)
23
+
24
+ producer = c.init_producer()
25
+ consumer = c.init_consumer(enable_auto_commit=False)
26
+
27
+ def send_msg():
28
+ while True:
29
+ command = input('发送消息: Y/n \n')
30
+ if command.strip() not in ['N', 'n']:
31
+ producer.send_msg('jqxx', '{{"jqid": "{}", "xxxx": "{}"}}'.format(string_tools.get_snowflake_id(), datetime.strftime(datetime.now(), '%Y-%m-%d %H:%M:%S')))
32
+ else:
33
+ break
34
+
35
+ thread_pool.submit(send_msg)
36
+
37
+ def consumer_callback(msg):
38
+ print(msg)
39
+ return True
40
+
41
+ consumer.receive_msg('jqxx', callBack=consumer_callback)
42
+
43
+ while True: time.sleep(1)
44
+ """
45
+ class KafkaInstance:
46
+ def __init__(self, producer: KafkaProducer, consumer: KafkaConsumer):
47
+ self.start_consumer = False
48
+ self.quited = False
49
+ self.producer = producer
50
+ self.consumer = consumer
51
+ self.consumer_callback = {"topic_key": []}
52
+
53
+ # FutureRecordMetadata 可以添加回调, 来监听是否发送成功
54
+ # r.add_callback(lambda x: print(x))
55
+ # r.get() 可以同步获取结果
56
+ def send_msg(self, topic, msg, key: str=None, partition:int=None) -> FutureRecordMetadata:
57
+ if self.producer is None: raise RuntimeError("Producer is not initialized")
58
+ if self.quited: return
59
+ return self.producer.send(topic=topic, value=msg, key=None if key is None else key.encode('utf-8'), partition=partition)
60
+
61
+ def receive_msg(self, topics: str, callBack=print):
62
+ if self.consumer is None: raise RuntimeError("Consumer is not initialized")
63
+ for topic in topics.split(','):
64
+ if topic not in self.consumer_callback.keys():
65
+ self.consumer_callback[topic] = []
66
+ self.consumer.subscribe(self.consumer_callback.keys())
67
+ self.consumer_callback[topic].append(callBack)
68
+ if not self.start_consumer:
69
+ t = Thread(target=self._start_consumer_poll, daemon=True)
70
+ t.start()
71
+
72
+ def _start_consumer_poll(self):
73
+ self.start_consumer = True
74
+ for msg in self.consumer:
75
+ if self.quited: break
76
+ funcList = []
77
+ begin_time = time.time()
78
+ for func in self.consumer_callback[msg.topic]:
79
+ if self.quited: break
80
+ res = func(msg)
81
+ if not self.consumer.config['enable_auto_commit'] and res: self.consumer.commit()
82
+ funcList.append(func.__name__)
83
+ end_time = time.time()
84
+ if end_time - begin_time > 1: print(f"kafka consume too slow!!! {funcList} time cost: ", f'{round(end_time - begin_time, 2)}s')
85
+ funcList.clear()
86
+
87
+ def shutdown(self):
88
+ self.quited = True
89
+ try: self.consumer.close()
90
+ except Exception: pass
91
+ try: self.producer.close()
92
+ except Exception: pass
93
+
94
+
95
+ class CKafka:
96
+
97
+ def __init__(self, kafka_url: str = '127.0.0.1:9092', secure: bool = False, username: str = 'client', password: str = 'hylink_user_password'):
98
+ self.kafka_url = kafka_url
99
+ self.secure = secure
100
+ self.username = username
101
+ self.password = password
102
+
103
+ def init_producer(self, acks=1) -> KafkaInstance:
104
+ print("[ Producer ] Connecting to Kafka [{}]".format(self.kafka_url))
105
+ for i in range(0, 6):
106
+ try:
107
+ if self.secure:
108
+ producer = KafkaProducer(
109
+ acks=acks,
110
+ bootstrap_servers=self.kafka_url,
111
+ value_serializer=lambda x: dumps(x).encode('utf-8'),
112
+ sasl_plain_username=self.username,
113
+ sasl_plain_password=self.password,
114
+ security_protocol='SASL_PLAINTEXT',
115
+ sasl_mechanism='PLAIN'
116
+ )
117
+ else:
118
+ producer = KafkaProducer(
119
+ acks=acks,
120
+ bootstrap_servers=self.kafka_url,
121
+ value_serializer=lambda x: dumps(x).encode('utf-8')
122
+ )
123
+ print("[ Producer ] Success Connected to Kafka [{}]".format(self.kafka_url))
124
+ return KafkaInstance(producer=producer, consumer=None)
125
+ except errors.NoBrokersAvailable:
126
+ print("[ Producer ] Waiting for Kafka [{}] to become available...".format(self.kafka_url))
127
+ time.sleep(3)
128
+ raise RuntimeError("[ Producer ] Failed to connect to Kafka [{}] within 60 seconds".format(self.kafka_url))
129
+
130
+ def init_consumer(self, client_id: str = 'ck-py-kafka-consumer', consumer_group: str = 'ck-py-kafka-consumer', enable_auto_commit: bool = True) -> KafkaInstance:
131
+ print("[ Consumer ] Connecting to Kafka [{}]".format(self.kafka_url))
132
+ for i in range(0, 6):
133
+ try:
134
+ if self.secure:
135
+ consumer = KafkaConsumer(
136
+ client_id=client_id,
137
+ group_id=consumer_group,
138
+ enable_auto_commit=enable_auto_commit,
139
+ bootstrap_servers=self.kafka_url,
140
+ value_deserializer=lambda x: x.decode('utf-8'),
141
+ sasl_plain_username=self.username,
142
+ sasl_plain_password=self.password,
143
+ security_protocol='SASL_PLAINTEXT',
144
+ sasl_mechanism='PLAIN'
145
+ )
146
+ else:
147
+ consumer = KafkaProducer(
148
+ client_id=client_id,
149
+ group_id=consumer_group,
150
+ enable_auto_commit=enable_auto_commit,
151
+ bootstrap_servers=self.kafka_url,
152
+ value_deserializer=lambda x: x.decode('utf-8')
153
+ )
154
+ print("[ Consumer ] Success Connected to Kafka [{}]".format(self.kafka_url))
155
+ return KafkaInstance(producer=None, consumer=consumer)
156
+ except errors.NoBrokersAvailable:
157
+ print("[ Consumer ] Waiting for Kafka [{}] to become available...".format(self.kafka_url))
158
+ time.sleep(3)
159
+ raise RuntimeError("[ Consumer ] Failed to connect to Kafka [{}] within 60 seconds".format(self.kafka_url))
@@ -0,0 +1,18 @@
1
+ import importlib
2
+ import os
3
+ import time
4
+
5
+
6
+ def code_to_pyc(code: str, out_file_path: str):
7
+ file_name = os.path.split(out_file_path)[-1]
8
+ compiled_code = compile(code, file_name, 'exec')
9
+ bytecode = importlib._bootstrap_external._code_to_timestamp_pyc(compiled_code, time.time(), len(code))
10
+ with open(out_file_path, 'wb') as f:
11
+ f.write(bytecode)
12
+
13
+
14
+ def file_to_pyc(file_path: str, out_file_path: str):
15
+ with open(file_path, 'r') as f:
16
+ code = f.read()
17
+ if code:
18
+ code_to_pyc(code, out_file_path)
ctools/console.py ADDED
@@ -0,0 +1,55 @@
1
+ import logging
2
+ import sys
3
+ import tkinter as tk
4
+
5
+
6
+ class Console:
7
+
8
+ def __init__(self, master):
9
+ self.master = master
10
+
11
+ # 创建文本框和滚动条
12
+ self.textbox = tk.Text(self.master, wrap=tk.NONE)
13
+
14
+ self.vertical_scrollbar = tk.Scrollbar(self.textbox, command=self.textbox.yview)
15
+ self.horizontal_scrollbar = tk.Scrollbar(self.textbox, command=self.textbox.xview, orient=tk.HORIZONTAL)
16
+
17
+ self.textbox.configure(yscrollcommand=self.vertical_scrollbar.set, xscrollcommand=self.horizontal_scrollbar.set)
18
+ self.textbox.pack(side=tk.LEFT, pady=10, padx=10, ipadx=10, ipady=10, fill=tk.BOTH, expand=True)
19
+
20
+ self.vertical_scrollbar.pack(side=tk.RIGHT, fill=tk.Y)
21
+ self.horizontal_scrollbar.pack(side=tk.BOTTOM, fill=tk.X)
22
+
23
+ # 将标准输出和标准错误输出重定向到文本框中
24
+ sys.stdout = self
25
+ sys.stderr = self
26
+
27
+ # # 创建输入框和按钮
28
+ # self.entry = tk.Entry(self.master)
29
+ # self.entry.pack(side=tk.BOTTOM, fill=tk.X, expand=True)
30
+ # self.button = tk.Button(self.master, text="Send", command=self.send)
31
+ # self.button.pack(side=tk.BOTTOM)
32
+
33
+ # 将日志输出到文本框中
34
+ self.log_handler = logging.StreamHandler(self)
35
+ self.log_handler.setFormatter(logging.Formatter('%(asctime)s - %(message)s'))
36
+ logging.getLogger().addHandler(self.log_handler)
37
+ # logging.getLogger().setLevel(logging.INFO)
38
+
39
+ def write(self, message):
40
+ # 在文本框中输出消息
41
+ self.textbox.insert(tk.END, message + '\n')
42
+ self.textbox.see(tk.END)
43
+
44
+ def flush(self):
45
+ pass
46
+
47
+ def send(self):
48
+ # 获取输入框中的文本并打印到控制台
49
+ text = self.entry.get()
50
+ print(text)
51
+ self.entry.delete(0, tk.END)
52
+
53
+ def __del__(self):
54
+ # 关闭日志处理器
55
+ logging.getLogger().removeHandler(self.log_handler)
ctools/coord_trans.py ADDED
@@ -0,0 +1,127 @@
1
+ # -*- coding: utf-8 -*-
2
+ import math
3
+
4
+ x_pi = 3.14159265358979324 * 3000.0 / 180.0
5
+ pi = 3.1415926535897932384626 # π
6
+ a = 6378245.0 # 长半轴
7
+ ee = 0.00669342162296594323 # 偏心率平方
8
+
9
+ def gcj02_to_bd09(lng, lat):
10
+ """
11
+ 火星坐标系(GCJ-02)转百度坐标系(BD-09)
12
+ 谷歌、高德——>百度
13
+ :param lng:火星坐标经度
14
+ :param lat:火星坐标纬度
15
+ :return:
16
+ """
17
+ z = math.sqrt(lng * lng + lat * lat) + 0.00002 * math.sin(lat * x_pi)
18
+ theta = math.atan2(lat, lng) + 0.000003 * math.cos(lng * x_pi)
19
+ bd_lng = z * math.cos(theta) + 0.0065
20
+ bd_lat = z * math.sin(theta) + 0.006
21
+ return [bd_lng, bd_lat]
22
+
23
+
24
+ def bd09_to_gcj02(bd_lon, bd_lat):
25
+ """
26
+ 百度坐标系(BD-09)转火星坐标系(GCJ-02)
27
+ 百度——>谷歌、高德
28
+ :param bd_lat:百度坐标纬度
29
+ :param bd_lon:百度坐标经度
30
+ :return:转换后的坐标列表形式
31
+ """
32
+ x = bd_lon - 0.0065
33
+ y = bd_lat - 0.006
34
+ z = math.sqrt(x * x + y * y) - 0.00002 * math.sin(y * x_pi)
35
+ theta = math.atan2(y, x) - 0.000003 * math.cos(x * x_pi)
36
+ gg_lng = z * math.cos(theta)
37
+ gg_lat = z * math.sin(theta)
38
+ return [gg_lng, gg_lat]
39
+
40
+
41
+ def wgs84_to_gcj02(lng, lat):
42
+ """
43
+ WGS84转GCJ02(火星坐标系)
44
+ :param lng:WGS84坐标系的经度
45
+ :param lat:WGS84坐标系的纬度
46
+ :return:
47
+ """
48
+ if out_of_china(lng, lat): # 判断是否在国内
49
+ return [lng, lat]
50
+ dlat = _transformlat(lng - 105.0, lat - 35.0)
51
+ dlng = _transformlng(lng - 105.0, lat - 35.0)
52
+ radlat = lat / 180.0 * pi
53
+ magic = math.sin(radlat)
54
+ magic = 1 - ee * magic * magic
55
+ sqrtmagic = math.sqrt(magic)
56
+ dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
57
+ dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
58
+ mglat = lat + dlat
59
+ mglng = lng + dlng
60
+ return [mglng, mglat]
61
+
62
+
63
+ def gcj02_to_wgs84(lng, lat):
64
+ """
65
+ GCJ02(火星坐标系)转GPS84
66
+ :param lng:火星坐标系的经度
67
+ :param lat:火星坐标系纬度
68
+ :return:
69
+ """
70
+ if out_of_china(lng, lat):
71
+ return [lng, lat]
72
+ dlat = _transformlat(lng - 105.0, lat - 35.0)
73
+ dlng = _transformlng(lng - 105.0, lat - 35.0)
74
+ radlat = lat / 180.0 * pi
75
+ magic = math.sin(radlat)
76
+ magic = 1 - ee * magic * magic
77
+ sqrtmagic = math.sqrt(magic)
78
+ dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * pi)
79
+ dlng = (dlng * 180.0) / (a / sqrtmagic * math.cos(radlat) * pi)
80
+ mglat = lat + dlat
81
+ mglng = lng + dlng
82
+ return [lng * 2 - mglng, lat * 2 - mglat]
83
+
84
+
85
+ def bd09_to_wgs84(bd_lon, bd_lat):
86
+ lon, lat = bd09_to_gcj02(bd_lon, bd_lat)
87
+ return gcj02_to_wgs84(lon, lat)
88
+
89
+
90
+ def wgs84_to_bd09(lon, lat):
91
+ lon, lat = wgs84_to_gcj02(lon, lat)
92
+ return gcj02_to_bd09(lon, lat)
93
+
94
+
95
+ def _transformlat(lng, lat):
96
+ ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + \
97
+ 0.1 * lng * lat + 0.2 * math.sqrt(math.fabs(lng))
98
+ ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
99
+ math.sin(2.0 * lng * pi)) * 2.0 / 3.0
100
+ ret += (20.0 * math.sin(lat * pi) + 40.0 *
101
+ math.sin(lat / 3.0 * pi)) * 2.0 / 3.0
102
+ ret += (160.0 * math.sin(lat / 12.0 * pi) + 320 *
103
+ math.sin(lat * pi / 30.0)) * 2.0 / 3.0
104
+ return ret
105
+
106
+
107
+ def _transformlng(lng, lat):
108
+ ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + \
109
+ 0.1 * lng * lat + 0.1 * math.sqrt(math.fabs(lng))
110
+ ret += (20.0 * math.sin(6.0 * lng * pi) + 20.0 *
111
+ math.sin(2.0 * lng * pi)) * 2.0 / 3.0
112
+ ret += (20.0 * math.sin(lng * pi) + 40.0 *
113
+ math.sin(lng / 3.0 * pi)) * 2.0 / 3.0
114
+ ret += (150.0 * math.sin(lng / 12.0 * pi) + 300.0 *
115
+ math.sin(lng / 30.0 * pi)) * 2.0 / 3.0
116
+ return ret
117
+
118
+
119
+ def out_of_china(lng, lat):
120
+ """
121
+ 判断是否在国内,不在国内不做偏移
122
+ :param lng:
123
+ :param lat:
124
+ :return:
125
+ """
126
+ return not (lng > 73.66 and lng < 135.05 and lat > 3.86 and lat < 53.55)
127
+