├── images
├── env.png
├── oss2.png
├── pip.png
├── pysmb.png
├── pipEnv.png
├── UpdateVTPK.png
├── UpdateVTS.png
├── paramiko.png
├── UpdateVTOSS.png
├── CreateAdvVTPK.png
├── CreatePartVTPK.png
├── 20180720_ChangeLog1_OptimaizedErrorHandling.png
└── 20180720_ChangeLog2_AddLODSpecifiedBoxandErrorHandling.png
├── python
├── advancedVectorTilePackage.tbx
├── updateVectorTileforOSS.py
├── updateVectorTilePackage.py
├── createAdvancedVectorTilePacakge.py
├── createPartVectorTilePackage.py
└── updateVectorTileService.py
├── .idea
├── misc.xml
├── vcs.xml
├── modules.xml
├── PartiallyUpateArcGISVectorTilesTools.iml
└── workspace.xml
├── LICENSE
├── .gitignore
└── README.md
/images/env.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/env.png
--------------------------------------------------------------------------------
/images/oss2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/oss2.png
--------------------------------------------------------------------------------
/images/pip.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/pip.png
--------------------------------------------------------------------------------
/images/pysmb.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/pysmb.png
--------------------------------------------------------------------------------
/images/pipEnv.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/pipEnv.png
--------------------------------------------------------------------------------
/images/UpdateVTPK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/UpdateVTPK.png
--------------------------------------------------------------------------------
/images/UpdateVTS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/UpdateVTS.png
--------------------------------------------------------------------------------
/images/paramiko.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/paramiko.png
--------------------------------------------------------------------------------
/images/UpdateVTOSS.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/UpdateVTOSS.png
--------------------------------------------------------------------------------
/images/CreateAdvVTPK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/CreateAdvVTPK.png
--------------------------------------------------------------------------------
/images/CreatePartVTPK.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/CreatePartVTPK.png
--------------------------------------------------------------------------------
/python/advancedVectorTilePackage.tbx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/python/advancedVectorTilePackage.tbx
--------------------------------------------------------------------------------
/images/20180720_ChangeLog1_OptimaizedErrorHandling.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/20180720_ChangeLog1_OptimaizedErrorHandling.png
--------------------------------------------------------------------------------
/.idea/misc.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
--------------------------------------------------------------------------------
/.idea/vcs.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
--------------------------------------------------------------------------------
/images/20180720_ChangeLog2_AddLODSpecifiedBoxandErrorHandling.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/makeling/PartiallyUpateArcGISVectorTilesTools/HEAD/images/20180720_ChangeLog2_AddLODSpecifiedBoxandErrorHandling.png
--------------------------------------------------------------------------------
/.idea/modules.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/.idea/PartiallyUpateArcGISVectorTilesTools.iml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 makeling
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 |
--------------------------------------------------------------------------------
/.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 | env/
12 | build/
13 | develop-eggs/
14 | dist/
15 | downloads/
16 | eggs/
17 | .eggs/
18 | lib/
19 | lib64/
20 | parts/
21 | sdist/
22 | var/
23 | wheels/
24 | *.egg-info/
25 | .installed.cfg
26 | *.egg
27 |
28 | # PyInstaller
29 | # Usually these files are written by a python script from a template
30 | # before PyInstaller builds the exe, so as to inject date/other infos into it.
31 | *.manifest
32 | *.spec
33 |
34 | # Installer logs
35 | pip-log.txt
36 | pip-delete-this-directory.txt
37 |
38 | # Unit test / coverage reports
39 | htmlcov/
40 | .tox/
41 | .coverage
42 | .coverage.*
43 | .cache
44 | nosetests.xml
45 | coverage.xml
46 | *.cover
47 | .hypothesis/
48 |
49 | # Translations
50 | *.mo
51 | *.pot
52 |
53 | # Django stuff:
54 | *.log
55 | local_settings.py
56 |
57 | # Flask stuff:
58 | instance/
59 | .webassets-cache
60 |
61 | # Scrapy stuff:
62 | .scrapy
63 |
64 | # Sphinx documentation
65 | docs/_build/
66 |
67 | # PyBuilder
68 | target/
69 |
70 | # Jupyter Notebook
71 | .ipynb_checkpoints
72 |
73 | # pyenv
74 | .python-version
75 |
76 | # celery beat schedule file
77 | celerybeat-schedule
78 |
79 | # SageMath parsed files
80 | *.sage.py
81 |
82 | # dotenv
83 | .env
84 |
85 | # virtualenv
86 | .venv
87 | venv/
88 | ENV/
89 |
90 | # Spyder project settings
91 | .spyderproject
92 | .spyproject
93 |
94 | # Rope project settings
95 | .ropeproject
96 |
97 | # mkdocs documentation
98 | /site
99 |
100 | # mypy
101 | .mypy_cache/
102 | .idea/workspace.xml
103 | .idea/workspace.xml
104 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Read Me
2 |
3 | ## Installation Guild
4 |
5 | - Requirement : ArcGIS Pro 2.0 or later; Pysmb; oss2; paramiko
6 |
7 | - Pip installation required. more information: https://pip.pypa.io/en/stable/installing/
8 |
9 | - Optional: Add python and pip path to Windows Environments.
10 |
11 | - PYSMB, OSS2, PARAMIKO packages required. we strongly recommend the packages installed via pip.
12 |
13 | `pip install pysmb`
14 |
15 | `pip install oss2`
16 |
17 | `pip install paramiko`
18 |
19 |
20 |
21 | ## Introduction
22 |
23 | **Partially Update ArcGIS Vector Tiles Toolbox** is based on ArcGIS Pro (version 2.0 or later), which is desktop end program that supports creating Vector Tile Package. This toolbox enables you to update vector tile package or services partially when the source data is in a pretty large amount.
24 |
25 | The toolbox includes 5 tools as below:
26 |
27 | ### Create Advanced Vector Tile Package
28 |
29 | This tool enables you to create an advanced vector tile package, which contains the Tiling Schema xml file and Tiling Indexed Polygon feature class. In the way , the schema is packaged with the tiles , so to ensure consistency for updating tiles partially. What's more, you can define Maximum Vertex Count to effect the levels of the tiles.
30 |
31 | ### Create Part Vector Tile Package
32 |
33 | This tool enables you to generate a partial vector tile package in the specified area of of the whole map extent. It requires that the Original Vector Tile Package created by the Create Advanced Vector Tile Package tool. The VTPK generated by the standard Create Vector Tile Package tool in ArcGIS Pro is not support for this tool.
34 |
35 | ### Update Vector Tile Package
36 |
37 | This tool enables you to update a vector tile package using the partial Vector Tile Package.
38 |
39 | ### Update Vector Tile Service
40 |
41 | This tool enables you to update a vector tile service using the partial Vector Tile Package. It doesn't require the tile layer service restarted, and the service can runs without interruption during the update .
42 |
43 | The ArcGIS Server Manager account and passwords, and tile service URL are required.
44 |
45 | ### Update Vector Tile for OSS
46 |
47 | This tool enables you to update a vector tile service on OSS using the partial Vector Tile Package. It doesn't require the tile layer service restarted, and the service can runs without interruption during the update .
48 |
49 | The Service Name, Access Key ID, Access Key Secret, Share Hostname, Bucket Name, Endpoint are required.
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
--------------------------------------------------------------------------------
/python/updateVectorTileforOSS.py:
--------------------------------------------------------------------------------
1 | # _*_ coding: utf-8 _*_
2 | # !/usr/bin/python
3 | __author__ = 'ma_keling'
4 | # Version : 1.1.0
5 | # Start Time : 2017-12-07
6 | # Update Time : 2018-7-20
7 | # Change Log :
8 | ##
9 |
10 | import os
11 | import sys
12 | import zipfile
13 | import arcpy
14 | import oss2
15 | import shutil
16 |
17 | # Defines the entry point into the script
18 | def main(argv=None):
19 | # input new part vtpk name
20 | newPartVtpkPath = arcpy.GetParameterAsText(0)
21 |
22 | # input update service name
23 | serviceName = arcpy.GetParameterAsText(1)
24 |
25 | # input oss access key id
26 | access_key_id = arcpy.GetParameterAsText(2)
27 |
28 | # input oss key password
29 | access_key_secret = arcpy.GetParameterAsText(3)
30 |
31 | # input bucket name
32 | bucket_name = arcpy.GetParameterAsText(4)
33 |
34 | # input endpoint
35 | endpoint = arcpy.GetParameterAsText(5)
36 |
37 | # testing parameters
38 | # newPartVtpkPath = "/Users/maklmac/Desktop/newPart.vtpk"
39 | # serviceName = 'makltest'
40 | # access_key_id = 'LTAI95MbCPCvRXjZ'
41 | # access_key_secret = '1bZNmbZzT9v8sUUqXC65VlfNeQVcRn'
42 | # bucket_name = 'zhoun'
43 | # endpoint = 'http://oss-cn-shanghai.aliyuncs.com'
44 |
45 | execute(newPartVtpkPath, serviceName, access_key_id, access_key_secret, bucket_name, endpoint)
46 |
47 | # Change vtpk extension from .vtpk to .zip
48 | def retype(newPartVtpkPath,newtype):
49 | try:
50 | filename = os.path.splitext(newPartVtpkPath)[0]; # file name
51 | filetype = os.path.splitext(newPartVtpkPath)[1]; # file type
52 | olddir = newPartVtpkPath
53 | newdir = filename + newtype
54 | os.rename(olddir, newdir)
55 | print("has changed:" + newdir)
56 |
57 | return newdir
58 | except:
59 | arcpy.AddError("retype failed: please provide a validates path")
60 |
61 | #uncompress the .zip file to folder
62 | def unzip(newPartZipPath):
63 | try:
64 | file_zip = zipfile.ZipFile(newPartZipPath, 'r')
65 | for file in file_zip.namelist():
66 | # print "unziping..."
67 | extractFolder = os.path.splitext(newPartZipPath)[0]
68 | file_zip.extract(file, extractFolder)
69 | file_zip.close()
70 | os.remove(newPartZipPath)
71 | print("unzip succeed!")
72 | return extractFolder
73 | except:
74 | arcpy.AddError("unzip failed, please provde a validates path")
75 | return False
76 |
77 | def zip_and_retype(newPartZipPath):
78 | try:
79 | prelen = len(newPartZipPath)
80 | # print(prelen)
81 | print("zip root folder: " + newPartZipPath)
82 |
83 | zipDir = newPartZipPath + ".zip"
84 | fp = zipfile.ZipFile(zipDir, mode='w')
85 | for parent, dirnames, filenames in os.walk(newPartZipPath):
86 | for filename in filenames:
87 | pathfile = os.path.join(parent, filename)
88 | arcname = pathfile[prelen:].strip(os.path.sep)
89 | fp.write(pathfile, arcname, compress_type=zipfile.ZIP_STORED)
90 | fp.close()
91 | print("zipDir:" + zipDir)
92 | retype(zipDir,".vtpk")
93 | return True
94 | except:
95 | arcpy.AddError("path or folderName not exit.")
96 |
97 | def delete_zip_folder(newPartZipPath):
98 | shutil.rmtree(newPartZipPath)
99 |
100 | # return the unzip local tile path include LODs
101 | def get_local_tile_path(newPartVtpkPath):
102 | newPartZipPath = retype(newPartVtpkPath,".zip")
103 | extractFolder = unzip(newPartZipPath)
104 | # for mac path
105 | # tilePath = extractFolder + '/p12/tile'
106 |
107 | # for windows path
108 | tilePath = os.path.join(extractFolder, 'p12\\tile')
109 | print("local new part tile path:", tilePath)
110 | return tilePath
111 |
112 | # connect OSS by access_key_id, access_key_secrest, bucket_name, endpoint
113 | def connect_OSS(access_key_id,access_key_secret,bucket_name,endpoint):
114 | # verify parameters
115 | for param in (access_key_id, access_key_secret, bucket_name, endpoint):
116 | assert '<' not in param, '请设置参数:' + param
117 |
118 | # create Bucket object
119 | bucket = oss2.Bucket(oss2.Auth(access_key_id, access_key_secret), endpoint, bucket_name)
120 |
121 | print("connect succeed: " + bucket_name)
122 |
123 | return bucket
124 |
125 | # upload bundles from local bundle files to OSS cache directories
126 | def oss_upload_bundles(bucket,filepath,bucket_path):
127 | file_list = os.listdir(filepath)
128 | for Level in file_list:
129 | lodPath = os.path.join(filepath,Level)
130 | bundles = os.listdir(lodPath)
131 | bucket_lod_path = os.path.join(bucket_path,Level)
132 | for bundle in bundles:
133 | local_bundle_path = os.path.join(lodPath,bundle)
134 | bucket_bundle_path = os.path.join(bucket_lod_path,bundle)
135 | result = bucket.put_object_from_file(bucket_bundle_path, local_bundle_path)
136 | print(local_bundle_path)
137 | print (bucket_bundle_path)
138 | print(result)
139 | return True
140 |
141 | # execute method for OSS
142 | def upload_bundle_In_OSS(filePath, bucketPath,access_key_id, access_key_secret, bucket_name,endpoint):
143 |
144 | bucket = connect_OSS(access_key_id, access_key_secret, bucket_name, endpoint)
145 | result = oss_upload_bundles(bucket, filePath, bucketPath)
146 | return result
147 |
148 | # get bucket cache path for specific service name
149 | def get_bucket_path(serviceName):
150 | bucket_path= 'agssitecache/VectorCache/Hosted/' + serviceName + '/VectorTileServer/tile'
151 | return bucket_path
152 |
153 |
154 | def execute(newPartVtpkPath, serviceName, access_key_id, access_key_secret, bucket_name,endpoint):
155 |
156 | try:
157 | #get local vector tile cache path
158 | filepath = get_local_tile_path(newPartVtpkPath)
159 |
160 | #get oss bucket path
161 | bucket_path = get_bucket_path(serviceName)
162 |
163 | #upload local vector tile cache to oss bucket store
164 | result = upload_bundle_In_OSS(filepath,bucket_path,access_key_id, access_key_secret, bucket_name,endpoint)
165 |
166 | if result:
167 | #zip newPathVTPKPath
168 | zip_and_retype(newPartVtpkPath)
169 | #delect zip folder
170 | delete_zip_folder(newPartVtpkPath)
171 | except:
172 | arcpy.AddError("execute failed!")
173 |
174 | if __name__ == "__main__":
175 | sys.exit(main(sys.argv[1:]))
176 |
177 |
178 |
179 |
--------------------------------------------------------------------------------
/python/updateVectorTilePackage.py:
--------------------------------------------------------------------------------
1 | # _*_ coding: utf-8 _*_
2 | # !/usr/bin/python
3 | __author__ = 'ma_keling'
4 | # Version : 1.1.0
5 | # Start Time : 2017-12-07
6 | # Update Time : 2018-7-20
7 | # Change Log :
8 | ##
9 |
10 | import os
11 | import zipfile
12 | import arcpy
13 | import shutil
14 | import sys
15 |
16 |
17 | # Defines the entry point into the script
18 | def main(argv=None):
19 | original_vtpk_path = arcpy.GetParameterAsText(0)
20 | update_vtpk_path = arcpy.GetParameterAsText(1)
21 | new_vtpk_path = arcpy.GetParameterAsText(2)
22 |
23 | # original_vtpk_path = r'D:\updateVT\workspace\china_100index.vtpk'
24 | # update_vtpk_path = r'D:\updateVT\workspace\newPart.vtpk'
25 | # new_vtpk_path = r'D:\updateVT\workspace\workspace\china_100index_new.vtpk'
26 |
27 | execute(new_vtpk_path, original_vtpk_path, update_vtpk_path)
28 |
29 |
30 | # Change vtpk extension from .vtpk to .zip
31 | def retype(workspace,newPartVtpkPath,newtype):
32 | try:
33 | filename = os.path.splitext(newPartVtpkPath)[0] # file name
34 | filetype = os.path.splitext(newPartVtpkPath)[1] # file type
35 | olddir = newPartVtpkPath
36 | newdir = filename + newtype
37 | os.rename(olddir, newdir)
38 | arcpy.AddMessage("has changed:" + newdir)
39 | return newdir
40 |
41 | except:
42 | delete_zip_folder(workspace)
43 | arcpy.AddError("retype failed: please provide a validates path")
44 |
45 |
46 |
47 |
48 | #uncompress the .zip file to folder
49 | def unzip(workspace,newPartZipPath):
50 | try:
51 | file_zip = zipfile.ZipFile(newPartZipPath, 'r')
52 | for file in file_zip.namelist():
53 | # print "unziping..."
54 | extractFolder = os.path.splitext(newPartZipPath)[0]
55 | file_zip.extract(file, extractFolder)
56 | file_zip.close()
57 | os.remove(newPartZipPath)
58 | print("unzip succeed!")
59 | return extractFolder
60 | except:
61 | delete_zip_folder(workspace)
62 | arcpy.AddError("unzip failed, please provide a validates path")
63 | return ""
64 |
65 | def zip_and_retype(workspace,original_extract_path,new_vtpk_name):
66 | try:
67 | prelen = len(original_extract_path)
68 | # print(prelen)
69 | print("zip root folder: " + original_extract_path)
70 |
71 | zipDir = original_extract_path + ".zip"
72 | fp = zipfile.ZipFile(zipDir, mode='w')
73 | for parent, dirnames, filenames in os.walk(original_extract_path):
74 | for filename in filenames:
75 | pathfile = os.path.join(parent, filename)
76 | arcname = pathfile[prelen:].strip(os.path.sep)
77 | fp.write(pathfile, arcname, compress_type=zipfile.ZIP_STORED)
78 | fp.close()
79 | print("zipDir:" + zipDir)
80 | olddir = retype(workspace,zipDir,".vtpk")
81 | newdir = os.path.join(os.path.dirname(olddir), new_vtpk_name)
82 | print("new dir:", newdir)
83 | os.rename(olddir, newdir)
84 | return True
85 | except:
86 | delete_zip_folder(workspace)
87 | arcpy.AddError("path or folderName not exit.")
88 |
89 | def delete_zip_folder(delete_path):
90 | shutil.rmtree(delete_path)
91 |
92 | def copy_files(workspace,oldvtpath=None, newvtpath=None):
93 | try:
94 | sourceDir = newvtpath
95 | targetDir = oldvtpath
96 |
97 | for f in os.listdir(sourceDir):
98 | sourceF = os.path.join(sourceDir, f)
99 | targetF = os.path.join(targetDir, f)
100 |
101 | if os.path.isfile(sourceF):
102 | if os.path.exists(targetF):
103 | open(targetF, "wb").write(open(sourceF, "rb").read())
104 | print("copy :" + targetF)
105 | arcpy.AddMessage("copy:"+ targetF)
106 | elif os.path.isdir(sourceF):
107 | if not os.path.exists(targetDir):
108 | os.mkdir(targetDir)
109 |
110 | copy_files(workspace, targetF, sourceF)
111 |
112 | print("Copy Succeed!")
113 | return True
114 | except:
115 | # delete_zip_folder(workspace)
116 | print("input vector tile path not exit")
117 |
118 | return False
119 |
120 | # return the unzip local tile path include LODs
121 | def get_tile_path(extract_folder):
122 | # # for windows path
123 | tilePath = os.path.join(extract_folder, 'p12\\tile')
124 | print("local new part tile path:", tilePath)
125 | return tilePath
126 |
127 |
128 | def make_workspace(workspace,original_vtpk_path,update_vtpk_path):
129 | original_vtpk_name = os.path.split(original_vtpk_path)[1]
130 | update_vtpk_name = os.path.split(update_vtpk_path)[1]
131 | original_vtpk_new_path = os.path.join(workspace,original_vtpk_name)
132 | update_vtpk_new_path = os.path.join(workspace,update_vtpk_name)
133 | open(original_vtpk_new_path,'wb').write(open(original_vtpk_path,'rb').read())
134 | open(update_vtpk_new_path,'wb').write(open(update_vtpk_path,'rb').read())
135 | print("new workspace created succeed!",workspace,original_vtpk_new_path,update_vtpk_new_path)
136 | return True
137 | # try:
138 | #
139 | # except:
140 | # print("create new workspace failed!")
141 |
142 |
143 | def execute(new_vtpk_path,original_vtpk_path,update_vtpk_path):
144 |
145 | # workspace = os.path.dirname(new_vtpk_path)
146 | dest_path = os.path.dirname(new_vtpk_path)
147 | workspace = os.path.join(os.path.dirname(original_vtpk_path),"workspace")
148 | if os.path.exists(workspace) == False:
149 | os.mkdir(workspace)
150 | # arcpy.CreateFolder_management(workspace)
151 | arcpy.AddMessage("Scratch!!!")
152 | original_vtpk_name = os.path.basename(original_vtpk_path)
153 | update_vtpk_name = os.path.basename(update_vtpk_path)
154 | new_vtpk_name = os.path.basename(new_vtpk_path)
155 | work_vtpk_path = os.path.join(workspace,new_vtpk_name)
156 | arcpy.AddMessage("creating workspace:" + workspace)
157 | result = make_workspace(workspace,original_vtpk_path, update_vtpk_path)
158 | arcpy.AddMessage("current workspace:" + workspace)
159 | if result:
160 | original_vtpk_new_path = os.path.join(workspace,original_vtpk_name)
161 | update_vtpk_new_path = os.path.join(workspace,update_vtpk_name)
162 | original_zip_path = retype(workspace,original_vtpk_new_path,".zip")
163 | update_zip_path = retype(workspace,update_vtpk_new_path,".zip")
164 | arcpy.AddMessage("retype the extension from .vtpk to .zip")
165 | print("original_zip_path:", original_zip_path)
166 | print("update_zip_path:", update_zip_path)
167 | original_unzip_path = unzip(workspace,original_zip_path)
168 | update_unzip_path = unzip(workspace,update_zip_path)
169 | arcpy.AddMessage("uncompress the zip files"+original_vtpk_path)
170 | print("original_unzip_path:" ,original_unzip_path)
171 | print("update_unzip_path:" ,update_unzip_path)
172 | original_tiles = get_tile_path(original_unzip_path)
173 | new_tiles = get_tile_path(update_unzip_path)
174 | arcpy.AddMessage("get the tiles path for update"+original_tiles+"\n"+new_tiles)
175 | # print("original_tiles", original_tiles)
176 | # print("new_tiles",new_tiles)
177 | arcpy.AddMessage("updating.......")
178 | copy_files(workspace,original_tiles,new_tiles)
179 |
180 | result = zip_and_retype(workspace,original_unzip_path,new_vtpk_name)
181 |
182 | arcpy.AddMessage("start copy...")
183 | shutil.copy(work_vtpk_path,new_vtpk_path)
184 |
185 | if result:
186 | delete_zip_folder(workspace)
187 |
188 |
189 | arcpy.AddMessage("update successfully!")
190 |
191 | if __name__ == "__main__":
192 | sys.exit(main(sys.argv[1:]))
193 |
194 |
--------------------------------------------------------------------------------
/python/createAdvancedVectorTilePacakge.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # !/usr/bin/python
3 | __author__ = 'mu_xiaoyan'
4 | # Version : 1.1.0
5 | # Start Time : 2017-12-07
6 | # Update Time : 2018-7-20
7 | # Change Log :
8 | ## 1. Replace arcpy.AddMessage() with arcpy.AddError() so to let the tool throw an exception automatically.
9 | ## 2. Optimized Error handling mechanism in Function createVtpkIndexAndPackage()
10 | ## 3. Fixed wrong error text during to old Error Handling logic.
11 |
12 |
13 | import arcpy
14 | import os
15 | import xml.dom.minidom as DOM
16 | import zipfile
17 | import shutil
18 | import sys
19 |
20 | # Update 2018-9-21 Drop the old logic
21 | # # Create Vector Tile Package Scheme for Customize Geographic Coordinate System
22 | # # Using 20 fixed Level for the first Version. It can be better in future version, if necessary.
23 | # def GenerateVtpkTilingScheme(in_map,tileScheme):
24 | # try:
25 | # scales = "295829355.454565;147914677.727283;73957338.8636413;36978669.4318207;18489334.7159103;9244667.35795516;4622333.67897758;2311166.83948879;1155583.4197444;577791.709872198;288895.854936099;144447.927468049;72223.9637340247;36111.9818670124;18055.9909335062;9027.99546675309;4513.99773337654;2256.99886668827;1128.49943334414;564.249716672068"
26 | # arcpy.server.GenerateMapServerCacheTilingScheme(in_map=in_map,
27 | # tile_origin="-180.0 180.0",
28 | # output_tiling_scheme=tileScheme,
29 | # num_of_scales=20,
30 | # scales=scales,
31 | # dots_per_inch=96,
32 | # tile_size="512 x 512")
33 | # arcpy.AddMessage("tile scheme - ready.")
34 | # return tileScheme
35 | # except:
36 | # arcpy.AddError("input map for tiling scheme invalid.")
37 | #
38 | # # Modify Scheme File to Avoid the tile_Origin Specification Bug of the Pro Tool
39 | # def modifyTilingSchemeFile(tileScheme):
40 | # try:
41 | # doc = DOM.parse(tileScheme)
42 | # tileOriginX = doc.getElementsByTagName('X')
43 | # tileOriginY = doc.getElementsByTagName('Y')
44 | # tileOriginX[0].firstChild.data = '-180'
45 | # tileOriginY[0].firstChild.data = '180'
46 | # f = open(tileScheme, 'w+')
47 | # doc.writexml(f)
48 | # f.close()
49 | # return True
50 | # except:
51 | # arcpy.AddError("tile scheme XML file does not exist.")
52 |
53 | # Create Vector Tile Index and then Create Vector Tile Package
54 | def create_vtpk_index_and_package(in_map,service_type,tile_scheme,vertex_count,index_polygon,outVtpk):
55 | try:
56 | arcpy.management.CreateVectorTileIndex(in_map=in_map,
57 | out_featureclass=index_polygon,
58 | service_type=service_type,
59 | tiling_scheme=tile_scheme,
60 | vertex_count=vertex_count)
61 | arcpy.AddMessage("tile index - ready.")
62 | arcpy.management.CreateVectorTilePackage(in_map=in_map,
63 | output_file=outVtpk,
64 | service_type=service_type,
65 | tiling_scheme=tile_scheme,
66 | tile_structure="INDEXED",
67 | min_cached_scale="",
68 | max_cached_scale="",
69 | index_polygons=index_polygon,
70 | summary=None,
71 | tags=None)
72 | arcpy.AddMessage("Pro standard tile package - ready!")
73 | return outVtpk
74 | # Update at 2018-7-20, optimized Error handling mechanism
75 | # Previous Code:
76 | # except:
77 | # arcpy.AddError("input map for packaging invalid. Check the coordinate system of the input map.")
78 | except arcpy.ExecuteError:
79 | severity = arcpy.GetMaxSeverity()
80 | if severity == 2:
81 | # If the tool returned an error
82 | arcpy.AddError("Error occurred \n{0}".format(arcpy.GetMessages(2)))
83 | elif severity == 1:
84 | # If the tool returned no errors, but returned a warning
85 | arcpy.AddWarning("Warning raised \n{0}".format(arcpy.GetMessages(1)))
86 | else:
87 | # If the tool did not return an error or a warning
88 | arcpy.AddMessage(arcpy.GetMessages())
89 |
90 | # Append the tiling scheme and index polygon to the standard vtpk
91 | def add_to_zip(original_zip, newfolder):
92 | try:
93 | # print("zip file: " + original_zip)
94 | folder = os.path.dirname(original_zip)
95 | prelen = len(folder)
96 | fp = zipfile.ZipFile(original_zip, mode='a')
97 | for parent, dirnames, filenames in os.walk(newfolder):
98 | for filename in filenames:
99 | pathfile = os.path.join(parent, filename)
100 | # print("pathfile",pathfile)
101 | arcname = pathfile[prelen:].strip(os.path.sep)
102 | # print("arcname",arcname)
103 | fp.write(pathfile, arcname)
104 | fp.close()
105 | return True
106 | except:
107 | arcpy.AddError("path or folderName not exist.")
108 |
109 | # Clear the scratch data
110 | def delete_zip_folder(delete_path):
111 | shutil.rmtree(delete_path)
112 |
113 | # Updated at 2018-9-21
114 | # Automatically finding xml file and allowing user to choose origin index polygon
115 | def get_tile_scheme_and_index(in_map):
116 | try:
117 | arcpy.AddMessage("# Starting finding existing XML file ...")
118 | aprx = arcpy.mp.ArcGISProject("CURRENT")
119 | map = aprx.listMaps(in_map)[0]
120 | # getting xml file
121 | map_sr = map.defaultCamera.getExtent().spatialReference
122 | map_sr_name = map_sr.name
123 | map_sr_wkid = map_sr.factoryCode
124 | vtpk_xml_name = "VTTS_"+str(map_sr_wkid)+"_"+map_sr_name+".xml"
125 | local_path = os.path.join(os.path.expanduser("~"), "AppData\Local\ESRI\Geoprocessing")
126 | vtpk_xml_path = os.path.join(local_path,vtpk_xml_name)
127 | arcpy.AddMessage("# Bingo! " + vtpk_xml_path + " has been found!")
128 | tile_scheme = vtpk_xml_path
129 | # getting service type
130 | if map_sr_wkid == 3857:
131 | service_type = "ONLINE"
132 | else:
133 | service_type = "EXISTING"
134 | # building index polygons
135 | common_aux_paras = [tile_scheme, service_type]
136 | return common_aux_paras
137 | except:
138 | arcpy.AddError("Failed to get tile scheme")
139 |
140 |
141 | # the main method of this script
142 | def execute(in_map,outVtpk,isOnline,vertex_count):
143 | vtpkDir = os.path.join(os.path.dirname(outVtpk), "AdvVtpkAuxFiles")
144 | # arcpy.AddMessage("TempFolder"+vtpkDir)
145 | if not os.path.exists(vtpkDir):
146 | os.makedirs(vtpkDir)
147 |
148 | # Updated 2018-9-21
149 | common_aux_paras = get_tile_scheme_and_index(in_map)
150 | tile_scheme = common_aux_paras[0]
151 | arcpy.AddMessage(tile_scheme)
152 | service_type = common_aux_paras[1]
153 | shutil.copy(tile_scheme,vtpkDir)
154 | index_polygon = vtpkDir + "\originMasterIndex.shp"
155 |
156 | # # Update 2018-9-21 Drop the old logic
157 | # if isOnline:
158 | # service_type = "ONLINE"
159 | # tileScheme = ""
160 | # arcpy.AddMessage("service type:"+service_type)
161 | # else:
162 | # service_type = "EXISTING"
163 | # arcpy.AddMessage("service type:"+service_type)
164 | # GenerateVtpkTilingScheme(in_map,tileScheme)
165 | # arcpy.AddMessage(tileScheme)
166 | # modifyTilingSchemeFile(tileScheme)
167 |
168 | originalVTPK = create_vtpk_index_and_package(in_map,service_type,tile_scheme,vertex_count,index_polygon,outVtpk)
169 | if add_to_zip(originalVTPK, vtpkDir):
170 | delete_zip_folder(vtpkDir)
171 |
172 | arcpy.AddMessage("advanced vector tile package has been generated.")
173 |
174 |
175 | def main(argv=None):
176 | # Input map in current project
177 | in_map = arcpy.GetParameterAsText(0)
178 | arcpy.AddMessage("Input map : {0}.".format(in_map))
179 |
180 | # Specify name and workspace for the output vtpk
181 | outVtpk = arcpy.GetParameterAsText(1)
182 | arcpy.AddMessage("Output advanced vtpk : {0}.".format(outVtpk))
183 |
184 | # Specify service type
185 | isOnline = arcpy.GetParameter(2)
186 | arcpy.AddMessage("isOnline:"+str(isOnline))
187 |
188 | # Specify maximum vertex count
189 | vertex_count = arcpy.GetParameterAsText(3)
190 | arcpy.AddMessage("Maximum vertex count : {0}.".format(vertex_count))
191 |
192 | execute(in_map,outVtpk,isOnline,vertex_count)
193 |
194 | if __name__ == "__main__":
195 | sys.exit(main(sys.argv[1:]))
--------------------------------------------------------------------------------
/python/createPartVectorTilePackage.py:
--------------------------------------------------------------------------------
1 | # -*- coding: utf-8 -*-
2 | # !/usr/bin/python
3 | __author__ = 'mu_xiaoyan'
4 | # Version : 2.1.0
5 | # Start Time : 2017-12-07
6 |
7 | # Update Time : 2018-09-20
8 | # Change Log :
9 | ## Add support for getting known XML
10 |
11 | # Update Time : 2018-8-13
12 | # Change Log :
13 | ## Added new logical to calculate affected bundle extent in each level to avoid vector tile lost
14 | ## New function - calculate_affected_indexes_layer(AOI, index_polygon)
15 | ## Perfectly maintained spatial relationship internal
16 | ## Obsoleted parameter LOD, which now is calculated automatically
17 | ## Automatically removed intermediate data and workspace
18 |
19 |
20 | import time, zipfile, arcpy
21 | import sys, os, shutil
22 |
23 |
24 | # uncompress the .zip file to folder
25 | def unzip(newPartZipPath):
26 | try:
27 | file_zip = zipfile.ZipFile(newPartZipPath, 'r')
28 | for file in file_zip.namelist():
29 | # print "unziping..."
30 | extractFolder = os.path.splitext(newPartZipPath)[0]
31 | file_zip.extract(file, extractFolder)
32 | file_zip.close()
33 | os.remove(newPartZipPath)
34 | #arcpy.AddMessage("unzip succeed!")
35 | return extractFolder
36 | except:
37 | arcpy.AddError("unzip failed, please provde a validates path")
38 | return ""
39 |
40 | # Analyzing Original vtpk file to get the tiling scheme and index polygon and also get the service type
41 | def analysis_original_vtpk(origin_vtpk_path):
42 |
43 | origin_workspace = os.path.dirname(origin_vtpk_path)
44 | origin_vtpk_name = os.path.basename(origin_vtpk_path)
45 | # Create temp workspace
46 | timeStamp = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
47 | temp_workspace = os.path.join(origin_workspace, str(timeStamp))
48 | os.mkdir(temp_workspace)
49 | try:
50 | # copy original vtpk
51 | bak_original_vtpk = shutil.copy(origin_vtpk_path, temp_workspace)
52 | unzip(bak_original_vtpk)
53 | # Locate vtpk aux files
54 | tile_scheme_name = "customizedScheme.xml"
55 | index_polygon_name = "originMasterIndex.shp"
56 | vtpk_extract_dir = origin_vtpk_name.split(".")[0]
57 | arcpy.AddMessage(vtpk_extract_dir)
58 | aux_files_path = temp_workspace+ r"\\"+ vtpk_extract_dir+ r"\AdvVtpkAuxFiles"
59 | index_polygon = os.path.join(aux_files_path, index_polygon_name)
60 | tile_scheme = os.path.join(aux_files_path, tile_scheme_name)
61 | # get the service type
62 | if os.path.exists(tile_scheme):
63 | service_type = "EXISTING"
64 | else:
65 | tile_scheme = ""
66 | service_type = "ONLINE"
67 | adv_aux_paras = [index_polygon, tile_scheme, service_type,temp_workspace]
68 | return adv_aux_paras
69 | except:
70 | arcpy.AddError("Original vtpk does not exist.")
71 |
72 | # Updated at 2018-9-21
73 | # Automatically finding xml file and allowing user to choose origin index polygon
74 | def get_tile_scheme_and_index(in_map):
75 | try:
76 | arcpy.AddMessage("# Starting finding existing XML file ...")
77 | aprx = arcpy.mp.ArcGISProject("CURRENT")
78 | map = aprx.listMaps(in_map)[0]
79 | # getting xml file
80 | map_sr = map.defaultCamera.getExtent().spatialReference
81 | map_sr_name = map_sr.name
82 | map_sr_wkid = map_sr.factoryCode
83 | vtpk_xml_name = "VTTS_"+str(map_sr_wkid)+"_"+map_sr_name+".xml"
84 | local_path = os.path.join(os.path.expanduser("~"), "AppData\Local\ESRI\Geoprocessing")
85 | vtpk_xml_path = os.path.join(local_path,vtpk_xml_name)
86 | arcpy.AddMessage("# Bingo! " + vtpk_xml_path + " has been found!")
87 | tile_scheme = vtpk_xml_path
88 | # getting service type
89 | if map_sr_wkid == 3857:
90 | service_type = "ONLINE"
91 | else:
92 | service_type = "EXISTING"
93 | # building index polygons
94 | common_aux_paras = [tile_scheme, service_type]
95 | return common_aux_paras
96 | except:
97 | arcpy.AddError("Failed to get tile scheme")
98 |
99 | # Updated at 2018-8-9
100 | # Added new function calculating the exact bundle extent in each level of vector tile
101 | def calculate_affected_indexes_layer(AOI,index_polygon):
102 | try:
103 | # in memory variables
104 | affected_indexes_fc = "in_memory/base_affected_indexes"
105 | statistics_LOD_tbl = "in_memory/statistics_LOD"
106 | bundle_polygon_each_level = "in_memory/bundle_index_polygon_each_level"
107 | timeStamp = time.strftime('%Y%m%d%H%M%S', time.localtime(time.time()))
108 | bundle_index_polygons = 'intermediate_indexes_' + str(timeStamp) + '.shp'
109 | arcpy.AddMessage(bundle_index_polygons + " Created ! Do not delete it whiling the tool is running.")
110 | AOI_lyr = arcpy.MakeFeatureLayer_management(AOI, "AOI_lyr_"+ str(timeStamp))
111 | index_polygon_lyr = arcpy.MakeFeatureLayer_management(index_polygon, "index_lyr_" + str(timeStamp))
112 |
113 | # Finding the max LOD
114 | arcpy.SelectLayerByLocation_management(index_polygon_lyr, 'INTERSECT', AOI_lyr)
115 | arcpy.Statistics_analysis(index_polygon_lyr, statistics_LOD_tbl, [["LOD", "MAX"]])
116 | statistics_LOD = [row[0] for row in arcpy.da.SearchCursor(statistics_LOD_tbl, ['MAX_LOD'])]
117 | max_lod = statistics_LOD[0]
118 | arcpy.AddMessage("- Max affected LOD detected "+str(max_lod))
119 | arcpy.AddMessage("- Partial vector indexes Creating ... ")
120 | # Finding indexed polygons overlaped AOI
121 | arcpy.CopyFeatures_management(index_polygon_lyr, affected_indexes_fc)
122 | arcpy.SelectLayerByAttribute_management(index_polygon_lyr,'CLEAR_SELECTION')
123 | base_affected_indexes_lyr = arcpy.MakeFeatureLayer_management(affected_indexes_fc, "base_affected_indexes_lyr")
124 | # Calculating bundle extent in each level
125 | arcpy.Select_analysis(index_polygon, bundle_index_polygons, '"LOD"<7')
126 | with arcpy.da.SearchCursor(affected_indexes_fc, ['SHAPE@','LOD']) as cursor:
127 | for row in cursor:
128 | if row[1]<=max_lod and row[1]>=7:
129 | bundle_lod = row[1]-7
130 | arcpy.SelectLayerByAttribute_management(base_affected_indexes_lyr, 'NEW_SELECTION',
131 | '"LOD" = '+str(bundle_lod))
132 | arcpy.SelectLayerByAttribute_management(index_polygon_lyr, 'NEW_SELECTION',
133 | '"LOD" = ' + str(row[1]))
134 | arcpy.SelectLayerByLocation_management(index_polygon_lyr, "WITHIN", base_affected_indexes_lyr, None,
135 | "SUBSET_SELECTION", "NOT_INVERT")
136 | arcpy.CopyFeatures_management(index_polygon_lyr, bundle_polygon_each_level)
137 | arcpy.Append_management([bundle_polygon_each_level], bundle_index_polygons, "NO_TEST")
138 | arcpy.Delete_management("in_memory")
139 | arcpy.Delete_management(AOI_lyr)
140 | arcpy.Delete_management(index_polygon_lyr)
141 | arcpy.Delete_management(base_affected_indexes_lyr)
142 | arcpy.AddMessage("- Partial vector indexes Generated !")
143 | return bundle_index_polygons
144 |
145 | except arcpy.ExecuteError:
146 | severity = arcpy.GetMaxSeverity()
147 | if severity == 2:
148 | # If the tool returned an error
149 | arcpy.AddError("Error occurred \n{0}".format(arcpy.GetMessages(2)))
150 | elif severity == 1:
151 | # If the tool returned no errors, but returned a warning
152 | arcpy.AddWarning("Warning raised \n{0}".format(arcpy.GetMessages(1)))
153 | else:
154 | # If the tool did not return an error or a warning
155 | arcpy.AddMessage(arcpy.GetMessages())
156 | return ""
157 |
158 | def main(argv=None):
159 | # Input map in the current project
160 | in_map = arcpy.GetParameterAsText(0)
161 | arcpy.AddMessage("# Input map : {0}.".format(in_map))
162 |
163 | # Specify the area where the delta new part vtpk need to be created
164 | AOI = arcpy.GetParameterAsText(1)
165 | arcpy.AddMessage("# AOI : {0}.".format(AOI))
166 |
167 | # Choose the existing original adv vtpk
168 | origin_vtpk = arcpy.GetParameterAsText(2)
169 | arcpy.AddMessage("# Original vtpk : {0}.".format(origin_vtpk))
170 |
171 | # Update 2018-9-20 add boolean parameter to check if the vtpk is advanced
172 | # Check if the vtpk is advanced
173 | is_adv_vtpk = arcpy.GetParameter(3)
174 | arcpy.AddMessage("# Is Advanced Vtpk : {0}.".format(is_adv_vtpk))
175 | # Specify the index polygon
176 | index_polygon = arcpy.GetParameter(4)
177 | arcpy.AddMessage("# Index Polygon path : {0}.".format(index_polygon))
178 |
179 | # Specify name and workspace for new part vtpk
180 | out_part_vtpk = arcpy.GetParameterAsText(5)
181 | arcpy.AddMessage("# New part vtpk : {0}.".format(out_part_vtpk))
182 |
183 | execute(in_map, AOI, origin_vtpk, is_adv_vtpk, index_polygon, out_part_vtpk)
184 |
185 | # Update 2018-8-10, Calling the new function
186 | # calculating the exact bundle extent in each level of vector tile
187 | def execute(in_map, AOI, origin_vtpk, is_adv_vtpk, index_polygon, out_part_vtpk):
188 |
189 | arcpy.env.workspace = os.path.dirname(out_part_vtpk)
190 | temp_workspace = None
191 |
192 | # Update 2018-9-20, add support for user defined xml file
193 | if is_adv_vtpk:
194 | # analyzing original_vtpk to get the scheme and index polygon feature class
195 | adv_aux_paras = analysis_original_vtpk(origin_vtpk)
196 | index_polygon = adv_aux_paras[0]
197 | tile_scheme = adv_aux_paras[1]
198 | service_type = adv_aux_paras[2]
199 | temp_workspace = adv_aux_paras[3]
200 | arcpy.AddMessage("- Analyzing origin vtpk succesfully! "
201 | + "\n - " + index_polygon + "\n - " + tile_scheme + "\n - " + service_type)
202 | else:
203 | common_aux_paras = get_tile_scheme_and_index(in_map)
204 | tile_scheme = common_aux_paras[0]
205 | service_type = common_aux_paras[1]
206 | arcpy.AddMessage("- Servcie Type: " + service_type)
207 |
208 | # Calculating affected bundle indexes
209 | bundle_index_polygons = calculate_affected_indexes_layer(AOI, index_polygon)
210 | arcpy.AddMessage("# Affected exactly index polygons created! ")
211 | arcpy.AddMessage("# Starting creating partial vector tiles ... ")
212 | arcpy.CreateVectorTilePackage_management(in_map=in_map,
213 | output_file=out_part_vtpk,
214 | service_type=service_type,
215 | tiling_scheme=tile_scheme,
216 | tile_structure="INDEXED",
217 | index_polygons=bundle_index_polygons)
218 | arcpy.AddMessage("# Partial vector tiles created! ")
219 |
220 | # Remove temporary intermediate data
221 | if os.path.exists(out_part_vtpk) and arcpy.Exists(out_part_vtpk):
222 | arcpy.Delete_management(os.path.join(arcpy.env.workspace, bundle_index_polygons))
223 | arcpy.AddMessage("Temp Data deleted - " + bundle_index_polygons)
224 | if os.path.exists(out_part_vtpk) and temp_workspace is not None:
225 | shutil.rmtree(temp_workspace)
226 | arcpy.AddMessage("Temp Data deleted - " + temp_workspace)
227 |
228 |
229 |
230 | if __name__ == "__main__":
231 | sys.exit(main(sys.argv[1:]))
232 |
--------------------------------------------------------------------------------
/python/updateVectorTileService.py:
--------------------------------------------------------------------------------
1 | # _*_ coding: utf-8 _*_
2 | # !/usr/bin/python
3 | __author__ = 'ma_keling'
4 | # Version : 1.1.0
5 | # Start Time : 2017-12-07
6 | # Update Time : 2018-7-20
7 | # Change Log :
8 | ##
9 |
10 | import os
11 | import sys
12 | import zipfile
13 | import shutil
14 | import requests
15 | import json
16 | import paramiko
17 | from smb.SMBConnection import *
18 | import arcpy
19 |
20 |
21 | def main(argv=None):
22 | update_vtpk_path = arcpy.GetParameterAsText(0)
23 | service_url = arcpy.GetParameterAsText(1)
24 | ags_username = arcpy.GetParameterAsText(2)
25 | ags_password = arcpy.GetParameterAsText(3)
26 | share_hostname=arcpy.GetParameterAsText(4)
27 | share_admin = arcpy.GetParameterAsText(5)
28 | share_password = arcpy.GetParameterAsText(6)
29 |
30 | # local part vtpk from windows system
31 | # update_cache_path = r'C:\makl\testdata\NewPart.vtpk'
32 |
33 | # for vector tile service from arcgis server 10.5 for windows
34 | # service_url = r'https://120win105.esrichina.com/server/rest/services/Hosted/china400W_vtpk/VectorTileServer'
35 |
36 | # for vector tile service from arcgis server 10.5 for linux
37 | # service_url = r"https://123linux106.esrichina.com:6443/arcgis/rest/services/Hosted/china400W_vtpk/VectorTileServer"
38 |
39 | # for arcgis server admin account
40 | # ags_username = 'arcgis'
41 | # ags_password = 'Super123'
42 |
43 | # cache directory stored in windows file system
44 | # share_hostname = '192.168.220.120'
45 | # share_admin = "administrator"
46 | # share_password = "Super123"
47 |
48 | # cache directory stored in linux file system
49 | # share_hostname = '192.168.220.123'
50 | # share_admin = "arcgis"
51 | # share_password = "Super123"
52 |
53 | execute(update_vtpk_path, service_url, ags_username, ags_password, share_hostname,
54 | share_admin, share_password)
55 |
56 | #common http tools
57 | #assistant method for submit request
58 | def submit_request(url,params,item=""):
59 | err_flag = 'failed'
60 | try:
61 | r = requests.post(url, data=params, verify=False)
62 |
63 | if (r.status_code != 200):
64 | r.raise_for_status()
65 | print('request failed.')
66 | return err_flag
67 | else:
68 | data = r.text
69 | elapse_time = str(r.elapsed.microseconds/1000/1000) + 's'
70 | # Check that data returned is not an error object
71 | if not assertJsonSuccess(data):
72 | return
73 | # Extract service list from data
74 | result = json.loads(data)
75 |
76 | if item != "":
77 | last_result = result[item]
78 | else:
79 | last_result = result
80 |
81 | # arcpy.AddMessage("response:"+str(last_result))
82 | return elapse_time,last_result
83 | except :
84 | arcpy.AddError("request failed")
85 | return err_flag
86 |
87 | # assert response json
88 | def assertJsonSuccess(data):
89 | obj = json.loads(data)
90 | if 'status' in obj and obj['status'] == "error":
91 | arcpy.AddError('Error: JSON object returns an error.' + str(obj))
92 | sys.exit(False)
93 | else:
94 | return True
95 |
96 | # connect_remote_linux_path(hostname, username, password, filepath, cache_path)
97 | def connect_remote_linux_path(hostname, username, password, filepath, cache_path):
98 | try:
99 | ##连接远程主机
100 | client = paramiko.SSHClient()
101 | client.set_missing_host_key_policy(paramiko.AutoAddPolicy())
102 | client.connect(hostname, 22, username, password)
103 |
104 | sftp = client.open_sftp()
105 |
106 | file_list = os.listdir(filepath)
107 | print(filepath)
108 | for Level in file_list:
109 | lodPath = os.path.join(filepath, Level)
110 | bundles = os.listdir(lodPath)
111 | server_lod_path = cache_path + '/' + Level
112 | for bundle in bundles:
113 | local_bundle_path = lodPath + os.sep + bundle
114 | server_bundle_path = server_lod_path + '/' + bundle
115 | print("local:",local_bundle_path)
116 | print("server:",server_bundle_path)
117 | sftp.put(local_bundle_path, server_bundle_path)
118 |
119 |
120 | client.close()
121 | return True
122 | except:
123 | arcpy.AddError("upload bundle failed!")
124 | return False
125 |
126 | # connect_remote_win_path(hostname,username,password,filepath,cache_path)
127 | def connect_remote_win_path(hostname,username,password,filepath,cache_path):
128 | localserver = "local"
129 | domain = ""
130 | service_name = ''
131 |
132 | try:
133 | if cache_path[:2] == r"\\":
134 | ll = cache_path.split('\\')
135 | service_name = ll[3]
136 | print("service_name")
137 | new_cache_path = ""
138 | for i in range(len(ll)):
139 | if i > 3:
140 | new_cache_path += "/"+ ll[i]
141 | print("new_cache_path:",new_cache_path)
142 |
143 | else:
144 | service_name = cache_path[:1] + "$"
145 | ll = cache_path.split('\\')
146 | new_cache_path = ""
147 | for i in range(len(ll)):
148 | if i > 0:
149 | new_cache_path += "/" + ll[i]
150 | print("new_cache_path:", new_cache_path)
151 |
152 | print("connecting server")
153 | conn = SMBConnection(username,password,localserver,hostname,domain=domain,
154 | use_ntlm_v2=True,sign_options=SMBConnection.SIGN_WHEN_SUPPORTED,is_direct_tcp=True)
155 |
156 | conn.connect(hostname, 445)
157 |
158 | response = conn.listShares(timeout=30) # obtain a list of shares
159 | print('Shares on: ' + hostname)
160 | arcpy.AddMessage('Shares on: ' + hostname)
161 |
162 | for i in range(len(response)): # iterate through the list of shares
163 | print(" Share[", i, "] =", response[i].name)
164 |
165 | print("filepath:", filepath)
166 |
167 | try:
168 |
169 | file_list = os.listdir(filepath)
170 |
171 | for Level in file_list:
172 | lodPath = os.path.join(filepath, Level)
173 | bundles = os.listdir(lodPath)
174 | server_lod_path = new_cache_path + "/" + Level
175 | for bundle in bundles:
176 | local_bundle_path = lodPath + os.sep + bundle
177 | server_bundle_path = server_lod_path + "/" + bundle
178 | print("local:",local_bundle_path)
179 | # print("server:", server_bundle_path)
180 | file_obj = open(local_bundle_path,'rb')
181 |
182 | # service_name = response[2].name
183 | print("service_name:",service_name)
184 | conn.storeFile(service_name, server_bundle_path, file_obj, timeout=60)
185 | print("uploaded:", server_bundle_path)
186 | arcpy.AddMessage("uploaded:"+server_bundle_path)
187 | file_obj.close()
188 |
189 |
190 | # print("upload over")
191 | conn.close()
192 | return True
193 | except:
194 | arcpy.AddError("upload failed! Maybe there is no any tiles in the dir: "+filepath)
195 | conn.close()
196 | return False
197 | except:
198 | conn.close()
199 | return False
200 |
201 | # get token by arcgis server
202 | def generateToken(url, username, password):
203 | tokenUrl = url + '/admin/generateToken'
204 | print(tokenUrl)
205 | # , 'ip':'192.168.100.85'
206 | params = {'username': username, 'password': password, 'client': 'requestip', 'f': 'json'}
207 |
208 | item = 'token'
209 |
210 | r = submit_request(tokenUrl, params, item)
211 |
212 | # arcpy.AddMessage("token:"+str(r[1]))
213 | return r[1]
214 |
215 | def get_cahces_list(url,token):
216 | directory_url = url + '/admin/system/directories'
217 | print('directory_url', directory_url)
218 |
219 | d_params = {'token': token, 'f': 'json'}
220 |
221 | result = submit_request(directory_url, d_params)
222 |
223 | directories = result[1]['directories']
224 |
225 | cache_dirs = []
226 |
227 | for dir in directories:
228 | if dir['directoryType'] == 'CACHE':
229 | print(dir)
230 | cache_dirs.append(dir['physicalPath'])
231 | print(dir['physicalPath'])
232 |
233 | return cache_dirs
234 |
235 | def get_cache_dir(url,token,service,item):
236 | # https://120win105.esrichina.com:6443/arcgis/admin/services/Hosted/mytest5000.VectorTileServer
237 | directory_url = url + '/admin/services/Hosted/'+service+".VectorTileServer"
238 | print('directory_url', directory_url)
239 |
240 | d_params = {'token': token, 'f': 'json'}
241 |
242 | result = submit_request(directory_url, d_params)
243 |
244 | # print("cache_result",result[1])
245 | cache_dir = ""
246 |
247 | cache_dir_main = result[1]['properties']['cacheDir']
248 | if cache_dir_main.__contains__('\\'):
249 | cache_dir = cache_dir_main + '\\VectorCache\\Hosted\\'+ service + '\\VectorTileServer\\tile'
250 | else:
251 | cache_dir = cache_dir_main + '/VectorCache/Hosted/'+ service + '/VectorTileServer/tile'
252 |
253 | # arcpy.AddMessage("token:" + str(cache_dir))
254 | return cache_dir
255 |
256 | # Defines the entry point into the script
257 |
258 | def parse_service_url(service_url):
259 | node_list = str(service_url).split('/')
260 | print(node_list)
261 | if len(node_list[2].split(":")) > 1:
262 | server_url = node_list[0]+'//'+node_list[2]+'/'+node_list[3]
263 | service_name = node_list[7]
264 | else:
265 | server_url = node_list[0]+'//'+node_list[2]+':6443/arcgis'
266 | service_name = node_list[7]
267 |
268 |
269 | return service_name,server_url
270 |
271 | # Change vtpk extension from .vtpk to .zip
272 | def retype(newPartVtpkPath,newtype):
273 | try:
274 | filename = os.path.splitext(newPartVtpkPath)[0]; # file name
275 | filetype = os.path.splitext(newPartVtpkPath)[1]; # file type
276 |
277 | olddir = newPartVtpkPath
278 | newdir = filename + newtype
279 |
280 | os.rename(olddir, newdir)
281 |
282 | return newdir
283 | except:
284 | arcpy.AddError("retype failed: please provide a validates path")
285 |
286 | #uncompress the .zip file to folder
287 | def unzip(newPartZipPath):
288 | try:
289 | file_zip = zipfile.ZipFile(newPartZipPath, 'r')
290 | for file in file_zip.namelist():
291 | # print "unziping..."
292 | extractFolder = os.path.splitext(newPartZipPath)[0]
293 | file_zip.extract(file, extractFolder)
294 | file_zip.close()
295 | os.remove(newPartZipPath)
296 | print("unzip succeed!")
297 | return extractFolder
298 | except:
299 | arcpy.AddError("unzip failed, please provde a validates path")
300 | return ""
301 |
302 | #zip and retype file
303 | def zip_and_retype(original_extract_path,new_vtpk_name):
304 | try:
305 | prelen = len(original_extract_path)
306 | # print(prelen)
307 | # print("zip root folder: " + original_extract_path)
308 |
309 | zipDir = original_extract_path + ".zip"
310 | fp = zipfile.ZipFile(zipDir, mode='w')
311 | for parent, dirnames, filenames in os.walk(original_extract_path):
312 | for filename in filenames:
313 | pathfile = os.path.join(parent, filename)
314 | arcname = pathfile[prelen:].strip(os.path.sep)
315 | fp.write(pathfile, arcname, compress_type=zipfile.ZIP_STORED)
316 | fp.close()
317 | # print("zipDir:" + zipDir)
318 | olddir = retype(zipDir,".vtpk")
319 | newdir = os.path.join(os.path.dirname(olddir), new_vtpk_name)
320 | # print("new dir:", newdir)
321 | os.rename(olddir, newdir)
322 | return True
323 | except:
324 | arcpy.AddError("path or folderName not exit.")
325 |
326 | def delete_zip_folder(delete_path):
327 | shutil.rmtree(delete_path)
328 |
329 | # upload bundles from local bundle files to OSS cache directories
330 | def upload_bundles(filepath, cache_path, hostname, username, password):
331 | result = False
332 |
333 | if cache_path[:1] == '/':
334 | result = connect_remote_linux_path(hostname, username, password, filepath, cache_path)
335 | elif cache_path[:2] == '\\':
336 | hostname_w = cache_path.split('\\')[2]
337 | result = connect_remote_win_path(hostname_w.username, password, filepath, cache_path)
338 | else:
339 | result = connect_remote_win_path(hostname, username, password, filepath, cache_path)
340 |
341 | return result
342 |
343 | # get unzip local .vtpk file and return local cache path
344 | def get_local_cache_path(upate_vtpk_path):
345 | zip_path = retype(upate_vtpk_path,'.zip')
346 | print("zip path:", zip_path)
347 | unzip_path = unzip(zip_path)
348 |
349 | if unzip_path[:1] == r"/":
350 | cache_path = os.path.join(unzip_path,'p12/tile')
351 | else:
352 | cache_path = os.path.join(unzip_path, 'p12/tile')
353 |
354 | return cache_path
355 |
356 | # execute method for arcgis gp tool
357 | def execute(update_vtpk_path,service_url,ags_username,ags_password,share_hostname,admin_username,admin_password):
358 | # arcpy.AddMessage('update starting...')
359 |
360 | update_vtpk_name = os.path.split(update_vtpk_path)[1]
361 | # arcpy.AddMessage('get update vtpk name: '+update_vtpk_name)
362 | print('get update vtpk name: '+update_vtpk_name)
363 |
364 | local_cache_path = get_local_cache_path(update_vtpk_path)
365 | # arcpy.AddMessage('get local part vector tile cache dir:'+local_cache_path)
366 | print('get local part vector tile cache dir:'+local_cache_path)
367 |
368 | service_name, server_url = parse_service_url(service_url)
369 | print("get service name : "+ service_name)
370 | print("get service url: " + server_url)
371 |
372 | token = generateToken(server_url, username=ags_username, password=ags_password)
373 |
374 | # arcpy.AddMessage('get service token: '+ token)
375 | print('get service token: ' + token)
376 |
377 | vt_path = get_cache_dir(server_url, token, service_name, 'cacheDir')
378 |
379 | # arcpy.AddMessage('get service tile cache dir: '+vt_path)
380 | print('get service tile cache dir: '+vt_path)
381 |
382 | # arcpy.AddMessage('uploading service tiles ...')
383 | print('uploading service tiles ...')
384 |
385 | if upload_bundles(local_cache_path, vt_path, share_hostname,admin_username,admin_password):
386 | # arcpy.AddMessage('upload cache successfully!')
387 | print('upload cache successfully!')
388 | unzip_path = update_vtpk_path.split(sep='.')[0]
389 | if zip_and_retype(unzip_path,update_vtpk_name):
390 | delete_zip_folder(unzip_path)
391 | # arcpy.AddMessage('All the update task finished!')
392 | print('All the update task finished!')
393 |
394 |
395 | if __name__ == "__main__":
396 | # sys.exit(main(sys.argv[1:]))
397 | sys.exit(main())
398 |
399 |
400 |
--------------------------------------------------------------------------------
/.idea/workspace.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | <<<<<<< HEAD
6 | <<<<<<< HEAD
7 | =======
8 |
9 |
10 |
11 | =======
12 | >>>>>>> 5611968ea3870391f7845ed27030be38f9393dde
13 |
14 |
15 |
16 |
17 | <<<<<<< HEAD
18 | =======
19 |
20 |
21 |
22 |
23 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
24 | =======
25 | >>>>>>> origin/master
26 | >>>>>>> 5611968ea3870391f7845ed27030be38f9393dde
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | <<<<<<< HEAD
40 |
41 |
42 |
43 |
44 | <<<<<<< HEAD
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 | =======
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 | <<<<<<< HEAD
81 | =======
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 |
95 |
96 |
97 |
98 |
99 |
100 |
101 |
102 |
103 |
104 |
105 |
106 |
107 |
108 |
109 |
110 |
111 |
112 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
113 | =======
114 | >>>>>>> origin/master
115 | >>>>>>> 5611968ea3870391f7845ed27030be38f9393dde
116 |
117 |
118 |
119 |
120 |
121 |
122 |
123 |
124 |
125 |
126 |
127 |
156 |
157 |
158 |
159 |
160 |
161 |
162 |
163 |
164 |
165 |
166 |
167 |
168 |
169 |
170 |
171 |
172 |
173 |
174 |
175 |
176 |
177 |
178 |
179 |
180 | <<<<<<< HEAD
181 |
182 |
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 | =======
194 |
195 |
196 |
197 |
198 |
199 |
200 |
201 |
202 |
203 |
204 |
205 |
206 |
207 |
208 |
209 |
210 |
211 |
212 |
213 |
214 |
215 |
216 |
217 |
218 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
219 |
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 |
228 |
229 |
230 |
231 |
232 |
233 |
234 |
235 |
236 |
237 |
238 |
239 |
240 |
241 |
242 |
243 |
244 |
245 |
246 |
247 |
248 |
249 |
250 |
251 |
252 |
253 |
254 |
255 |
256 |
257 |
258 |
259 |
260 |
261 |
262 |
263 |
264 |
265 |
266 |
267 |
268 |
269 |
270 |
271 |
272 |
273 |
274 |
275 |
276 |
277 |
278 |
279 |
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 |
289 |
290 |
291 |
292 |
293 |
294 |
295 |
296 |
297 |
298 |
299 |
300 |
301 |
302 |
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 |
314 |
315 |
316 |
317 |
318 |
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 |
328 |
329 |
330 |
331 |
332 |
333 |
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 |
343 |
344 |
345 |
346 |
347 |
348 |
349 |
350 |
351 |
352 |
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 |
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 |
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 | 1532056817804
387 |
388 |
389 | 1532056817804
390 |
391 |
392 |
393 |
394 | <<<<<<< HEAD
395 | <<<<<<< HEAD
396 |
397 | =======
398 |
399 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
400 | =======
401 |
402 | =======
403 |
404 | >>>>>>> origin/master
405 | >>>>>>> 5611968ea3870391f7845ed27030be38f9393dde
406 |
407 |
408 |
409 |
410 | <<<<<<< HEAD
411 |
412 |
413 |
414 |
415 |
416 |
417 |
418 |
419 | =======
420 |
421 |
422 |
423 |
424 |
425 |
426 |
427 |
428 | >>>>>>> origin/master
429 |
430 |
431 |
432 |
433 |
434 | <<<<<<< HEAD
435 | =======
436 |
437 |
438 |
439 |
440 |
441 | >>>>>>> origin/master
442 |
443 |
444 |
445 |
446 |
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 | <<<<<<< HEAD
456 |
457 |
458 |
459 |
460 |
461 |
462 |
463 |
464 |
465 |
466 |
467 |
468 |
469 |
470 | <<<<<<< HEAD
471 |
472 | =======
473 |
474 |
475 |
476 |
477 |
478 |
479 |
480 |
481 | <<<<<<< HEAD
482 | =======
483 |
484 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
485 | =======
486 | >>>>>>> origin/master
487 | >>>>>>> 5611968ea3870391f7845ed27030be38f9393dde
488 |
489 |
490 |
491 |
492 |
493 |
494 |
495 |
496 |
497 |
498 |
499 |
500 |
501 |
502 |
503 | <<<<<<< HEAD
504 |
505 |
506 |
507 |
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 | =======
518 |
519 |
520 |
521 |
522 |
523 | <<<<<<< HEAD
524 |
525 |
526 |
527 |
528 |
529 |
530 |
531 |
532 |
533 | =======
534 |
535 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
536 |
537 |
538 |
539 | >>>>>>> origin/master
540 |
541 |
542 |
543 | <<<<<<< HEAD
544 |
545 |
546 | <<<<<<< HEAD
547 | =======
548 |
549 |
550 | <<<<<<< HEAD
551 |
552 |
553 |
554 |
555 |
556 | =======
557 | >>>>>>> 5611968ea3870391f7845ed27030be38f9393dde
558 |
559 |
560 | >>>>>>> origin/master
561 |
562 |
563 |
564 |
565 |
566 | <<<<<<< HEAD
567 |
568 |
569 | =======
570 |
571 |
572 |
573 |
574 |
575 |
576 |
577 |
578 |
579 | >>>>>>> origin/master
580 |
581 |
582 |
583 |
584 |
585 |
586 |
587 |
588 |
589 |
590 |
591 |
592 |
593 |
594 | =======
595 |
596 |
597 |
598 |
599 |
600 |
601 |
602 |
603 |
604 |
605 |
606 |
607 |
608 |
609 |
610 |
611 |
612 |
613 |
614 |
615 |
616 |
617 | >>>>>>> 137ea50a2b2444a3035f23d241c37f2e796eb822
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 |
626 |
627 |
628 |
629 |
630 |
631 |
--------------------------------------------------------------------------------