Skip to content

Instantly share code, notes, and snippets.

@jdbcode
Last active September 18, 2023 20:41
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jdbcode/90021653ba0cac8a6eede87b8062f1a7 to your computer and use it in GitHub Desktop.
Save jdbcode/90021653ba0cac8a6eede87b8062f1a7 to your computer and use it in GitHub Desktop.
convert_js_to_py.ipynb
Display the source blob
Display the rendered blob
Raw
{
"cells": [
{
"cell_type": "markdown",
"metadata": {
"id": "view-in-github",
"colab_type": "text"
},
"source": [
"<a href=\"https://colab.research.google.com/gist/jdbcode/90021653ba0cac8a6eede87b8062f1a7/convert_js_to_py.ipynb\" target=\"_parent\"><img src=\"https://colab.research.google.com/assets/colab-badge.svg\" alt=\"Open In Colab\"/></a>"
]
},
{
"cell_type": "markdown",
"metadata": {
"id": "bra_MEILR0OW"
},
"source": [
"This Colab notebook is for converting Earth Engine JavaScript code examples to Python (with geemap for UI).\n",
"See this doc for more resources on conversion: https://docs.google.com/document/d/1ih_VzZPSqI6fVUvvF6pAluyduHkI1B6ReRxQ-lLRo2o/edit?usp=sharing"
],
"id": "bra_MEILR0OW"
},
{
"cell_type": "markdown",
"source": [
"Install Python code style library [Black](https://black.readthedocs.io/en/stable/index.html)."
],
"metadata": {
"id": "-seP7sBBrKrL"
},
"id": "-seP7sBBrKrL"
},
{
"cell_type": "code",
"source": [
"!pip install -q black"
],
"metadata": {
"id": "PQ306YSfrJod"
},
"id": "PQ306YSfrJod",
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"Imports and Earth Engine setup."
],
"metadata": {
"id": "wbJsQOSVrWwL"
},
"id": "wbJsQOSVrWwL"
},
{
"cell_type": "code",
"source": [
"import datetime\n",
"import re\n",
"import geemap\n",
"import ee\n",
"ee.Authenticate()\n",
"ee.Initialize()"
],
"metadata": {
"id": "IXpr-1Pd6iHG"
},
"id": "IXpr-1Pd6iHG",
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# @title ##### JavaScript to Python functions.\n",
"\n",
"def misc_replacement(input_string):\n",
" \"\"\"Makes misc replacements.\"\"\"\n",
"\n",
" replacements = [\n",
" (\"/**\", \"\"),\n",
" (\"\\n */\", \".\"),\n",
" (\"..\\n\", \".\\n\"),\n",
" (\" *\", \"#\"),\n",
" (\"//\", \"#\"),\n",
" (\"# https\", \"# https\"),\n",
" (\"@fileoverview\", \"\"),\n",
" (\"https:#\", \"https://\"),\n",
" (\"http:#\", \"https://\"),\n",
" (\"print(\", \"display(\"),\n",
" (\";\", \"\"),\n",
" (\"\\\"License\\\")\", \"\\\"License\\\");\"),\n",
" (\"var \", \"\"),\n",
" (\"Map.addLayer\", \"m.add_ee_layer\"),\n",
" (\"Map.setCenter\", \"m.set_center\"),\n",
" (\"Map.centerObject\", \"m.center_object\"),\n",
" (\"and(\", \"And(\"),\n",
" (\"or(\", \"Or(\"),\n",
" (\"not(\", \"Not(\"),\n",
" (\"false\", \"False\"),\n",
" (\"true\", \"True\"),\n",
" (\"null\", \"None\"),\n",
" (\"min:\", \"'min':\"),\n",
" (\"max:\", \"'max':\"),\n",
" (\"color:\", \"'color':\"),\n",
" (\"bands:\", \"'bands':\"),\n",
" (\"palette:\", \"'palette':\"),\n",
" (\"gamma:\", \"'gamma':\"),\n",
" ]\n",
"\n",
" for old_str, new_str in replacements:\n",
" input_string = input_string.replace(old_str, new_str)\n",
"\n",
" return input_string\n",
"\n",
"\n",
"def update_copyright_year(input_string):\n",
" current_year = datetime.datetime.now().year\n",
" updated_string = re.sub(r'Copyright \\d{4}', f'Copyright {current_year}', input_string)\n",
" return updated_string\n",
"\n",
"\n",
"def convert_var_snake_case(input_string):\n",
" \"\"\"Converts camelCase var names to snake case.\"\"\"\n",
"\n",
" lines = input_string.split('\\n')\n",
" variables = {}\n",
"\n",
" for i, line in enumerate(lines):\n",
" if line.startswith(\"var\"):\n",
" match = re.search(r'\\bvar\\s+(\\w+)\\s*=', line)\n",
" if match:\n",
" original_var_name = match.group(1)\n",
" snake_var_name = re.sub(r'(?<=[0-9])(?=[A-Za-z])|(?<=[A-Za-z])(?=[0-9])', r'_', original_var_name)\n",
" snake_var_name = re.sub(r'(?<=[a-z0-9])([A-Z])', r'_\\1', snake_var_name)\n",
" snake_var_name = snake_var_name.lower()\n",
" variables[original_var_name] = snake_var_name\n",
"\n",
" for orig_var, snake_var in variables.items():\n",
" input_string = input_string.replace(orig_var, snake_var)\n",
"\n",
" return input_string\n",
"\n",
"\n",
"def single_line_chain(input_string):\n",
" lines = input_string.split('\\n')\n",
" output_lines = []\n",
"\n",
" for i, line in enumerate(lines):\n",
" if i > 0 and line.strip().startswith('.'):\n",
" output_lines[-1] = output_lines[-1].rstrip() + ' \\\\'\n",
" output_lines.append(line)\n",
"\n",
" return '\\n'.join(output_lines)\n",
"\n",
"\n",
"def fix_map_function_lines(input_string):\n",
" lines = input_string.split('\\n')\n",
" output_lines = []\n",
"\n",
" for line in lines:\n",
" if \"map(function(\" in line:\n",
" line += \" <--- FIX THIS FUNCTION\"\n",
" output_lines.append(line)\n",
"\n",
" return '\\n'.join(output_lines)\n",
"\n",
"\n",
"def add_a_map(input_string):\n",
" lines = input_string.split('\\n')\n",
" output_lines = []\n",
" append_m = True\n",
"\n",
" for line in lines:\n",
" if line.strip().startswith(\"m.\") and append_m:\n",
" output_lines.append(\"m = geemap.Map()\")\n",
" append_m = False\n",
" output_lines.append(line)\n",
"\n",
"\n",
" string = '\\n'.join(output_lines)\n",
" # if not append_m:\n",
" # string = f'{string}m'\n",
"\n",
" return string\n",
"\n",
"def display_a_map(input_string):\n",
" lines = input_string.split('\\n')\n",
" last_m_index = -1\n",
"\n",
" for i, line in enumerate(lines):\n",
" if line.strip().startswith('m.'):\n",
" last_m_index = i\n",
"\n",
" if last_m_index != -1:\n",
" lines.insert(last_m_index + 1, 'm')\n",
"\n",
" updated_string = '\\n'.join(lines)\n",
" return updated_string\n",
"\n",
"\n",
"def replace_dict_with_params(input_string):\n",
" end_comma_pattern = r',(\\s*\\n\\s*})'\n",
" input_string = re.sub(end_comma_pattern, r'\\1', input_string)\n",
"\n",
"\n",
" dict_pattern = r'\\(\\n*\\s*\\{\\s*([^{}]+)\\s*\\}\\)'\n",
"\n",
" def replace(match):\n",
" key_value_pairs = match.group(1).split(',')\n",
" replaced_pairs = [f\"{pair.split(':')[0].strip()}={pair.split(':')[1].strip()}\" for pair in key_value_pairs]\n",
" return '(' + ', '.join(replaced_pairs) + ')'\n",
"\n",
" result_string = re.sub(dict_pattern, replace, input_string)\n",
" return result_string\n",
"\n",
"\n",
"def remove_newline(input_string):\n",
" pattern = r'=\\s*\\n+'\n",
" result = re.sub(pattern, '=', input_string)\n",
" return result\n",
"\n",
"\n",
"def convert_to_py(snippet, snake_case=True):\n",
" \"\"\"Converts common JS syntax to Py.\"\"\"\n",
"\n",
" if snake_case:\n",
" snippet = convert_var_snake_case(snippet)\n",
" snippet = convert_var_snake_case(snippet) # 2nd run is intended\n",
"\n",
" snippet = misc_replacement(snippet)\n",
" snippet = update_copyright_year(snippet)\n",
" snippet = single_line_chain(snippet)\n",
" snippet = fix_map_function_lines(snippet)\n",
" snippet = replace_dict_with_params(snippet)\n",
" snippet = snippet.replace(\"'color'=\", \"color=\")\n",
" snippet = remove_newline(snippet)\n",
"\n",
" if \"m.add_ee_layer\" in snippet:\n",
" snippet = add_a_map(snippet)\n",
" snippet = display_a_map(snippet)\n",
"\n",
" return '# Python code converted from JavaScript\\n\\n\\n' + snippet\n"
],
"metadata": {
"id": "FKsLZPWCj9-v",
"cellView": "form"
},
"id": "FKsLZPWCj9-v",
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"metadata": {
"id": "rjIpILWBR0OY"
},
"source": [
"#### 1. Copy and paste JavaScript code into a code block wrapped with trip quotes; declare as a variable."
],
"id": "rjIpILWBR0OY"
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {
"id": "W5eqqkbwR0OY"
},
"outputs": [],
"source": [
"js_snippet = \"\"\"\n",
"var dataset = ee.ImageCollection('IDAHO_EPSCOR/TERRACLIMATE')\n",
" .filter(ee.Filter.date('2017-07-01', '2017-08-01'));\n",
"var maximumTemperature = dataset.select('tmmx');\n",
"var maximumTemperatureVis = {\n",
" min: -300.0,\n",
" max: 300.0,\n",
" palette: [\n",
" '1a3678', '2955bc', '5699ff', '8dbae9', 'acd1ff', 'caebff', 'e5f9ff',\n",
" 'fdffb4', 'ffe6a2', 'ffc969', 'ffa12d', 'ff7c1f', 'ca531a', 'ff0000',\n",
" 'ab0000'\n",
" ],\n",
"};\n",
"Map.setCenter(71.72, 52.48, 3);\n",
"Map.addLayer(maximumTemperature, maximumTemperatureVis, 'Maximum Temperature');\n",
"\"\"\""
],
"id": "W5eqqkbwR0OY"
},
{
"cell_type": "markdown",
"source": [
"#### 2. Run the following cell to convert JavaScript to Python, copy the result into the next code cell."
],
"metadata": {
"id": "lWtpPvx0Stio"
},
"id": "lWtpPvx0Stio"
},
{
"cell_type": "code",
"source": [
"py_snippet = convert_to_py(js_snippet, snake_case=True)\n",
"print(py_snippet)"
],
"metadata": {
"id": "IBc5pTIzkMe3"
},
"id": "IBc5pTIzkMe3",
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"#### 3. Paste output from above into code cell below and attempt to run."
],
"metadata": {
"id": "3GPUPd1lTYS4"
},
"id": "3GPUPd1lTYS4"
},
{
"cell_type": "code",
"source": [
"# @title ##### Things to check for (converter should catch most):\n",
"\n",
"\"\"\"\n",
"### Constructing a `geemap.Map` object ###\n",
"# If the code sample displays map layers, you'll need to define a map object (right before the first time it is used)\n",
"m = geemap.Map()\n",
"m.set_center(-114.2579, 38.9275, 8)\n",
"m.add_ee_layer(dataset, visualization, 'True Color (432)')\n",
"m\n",
"\n",
"\n",
"### Multiline method chaining ###\n",
"# You'll need to wrap these in parenthesis (don't use \\\\ line continuation):\n",
"obj = (my.really()\n",
" .long()\n",
" .method()\n",
" .chain())\n",
"\n",
"\n",
"### Dictionary arguments ###\n",
"# You'll maybe need to add quotes around key names:\n",
"obj = ee.Dictionary({\"key\": value})\n",
"\n",
"\n",
"### Named parameters ###\n",
"# You'll need to use parameter names instead of dictionary:\n",
"obj = ee.ReduceRegion(\n",
" reducer=ee.Reducer.max(),\n",
" geometry=ee.Geometry.Point(0, 0),\n",
" scale=1)\n",
"\n",
"\n",
"### Named function definition ###\n",
"# You'll need to use Python syntax for named functions:\n",
"def fun_name(foo, bar):\n",
" print(foo, bar)\n",
" return None\n",
"\n",
"\n",
"### Anonymous function ###\n",
"# You'll need to use Python syntax for anonymous functions (use named function if multiline):\n",
"foo = col.map(lambda arg: arg)\n",
"\"\"\""
],
"metadata": {
"cellView": "form",
"id": "gMFKLokB_VK6"
},
"id": "gMFKLokB_VK6",
"execution_count": null,
"outputs": []
},
{
"cell_type": "code",
"source": [
"# Python code converted from JavaScript\n",
"\n",
"\n",
"\n",
"dataset = ee.ImageCollection('IDAHO_EPSCOR/TERRACLIMATE') \\\n",
" .filter(ee.Filter.date('2017-07-01', '2017-08-01'))\n",
"maximum_temperature = dataset.select('tmmx')\n",
"maximum_temperature_vis = {\n",
" 'min': -300.0,\n",
" 'max': 300.0,\n",
" 'palette': [\n",
" '1a3678', '2955bc', '5699ff', '8dbae9', 'acd1ff', 'caebff', 'e5f9ff',\n",
" 'fdffb4', 'ffe6a2', 'ffc969', 'ffa12d', 'ff7c1f', 'ca531a', 'ff0000',\n",
" 'ab0000'\n",
" ]\n",
"}\n",
"m = geemap.Map()\n",
"m.set_center(71.72, 52.48, 3)\n",
"m.add_ee_layer(maximum_temperature, maximum_temperature_vis, 'Maximum Temperature')\n",
"m\n"
],
"metadata": {
"id": "BmVr7ITltKYr"
},
"id": "BmVr7ITltKYr",
"execution_count": null,
"outputs": []
},
{
"cell_type": "markdown",
"source": [
"#### 4. Style the code according to [Black](https://black.readthedocs.io/en/stable/index.html).\n",
"\n"
],
"metadata": {
"id": "QHA5s83Bte0i"
},
"id": "QHA5s83Bte0i"
},
{
"cell_type": "code",
"source": [
"converted_code = [\n",
" s for s in _ih if s.startswith(\"# Python code converted from JavaScript\")\n",
"][-1].replace(\"# Python code converted from JavaScript\", \"\")\n",
"\n",
"!black --code \"{converted_code}\""
],
"metadata": {
"id": "SAD8QdYQvunM"
},
"id": "SAD8QdYQvunM",
"execution_count": null,
"outputs": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3",
"language": "python",
"name": "python3"
},
"colab": {
"provenance": [],
"toc_visible": true,
"include_colab_link": true
}
},
"nbformat": 4,
"nbformat_minor": 5
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment