To solve this problem, we first need to translate the natural language description into a symbolic representation. Let's denote the amount invested in the shoe factory as $x_1$ and the amount invested in the hat factory as $x_2$. The objective is to maximize earnings, which can be calculated as $0.07x_1 + 0.06x_2$, given that investments in the shoe factory earn 7% and investments in the hat factory earn 6%.

The constraints are:
1. The total amount invested cannot exceed $20,000: $x_1 + x_2 \leq 20000$.
2. The amount invested in the shoe factory must be at least 4 times the amount invested in the hat factory: $x_1 \geq 4x_2$.
3. The amount invested in the shoe factory cannot exceed $15,000: $x_1 \leq 15000$.

The symbolic representation of the problem is:
```json
{
    'sym_variables': [('x1', 'amount invested in the shoe factory'), ('x2', 'amount invested in the hat factory')],
    'objective_function': '0.07*x1 + 0.06*x2',
    'constraints': ['x1 + x2 <= 20000', 'x1 >= 4*x2', 'x1 <= 15000', 'x1 >= 0', 'x2 >= 0']
}
```

Now, let's write the Gurobi code to solve this linear programming problem:

```python
from gurobipy import *

# Create a new model
m = Model("Investment_Optimization")

# Define variables
x1 = m.addVar(lb=0, name="shoe_factory")
x2 = m.addVar(lb=0, name="hat_factory")

# Set the objective function
m.setObjective(0.07*x1 + 0.06*x2, GRB.MAXIMIZE)

# Add constraints
m.addConstr(x1 + x2 <= 20000, "total_investment")
m.addConstr(x1 >= 4*x2, "shoe_hat_ratio")
m.addConstr(x1 <= 15000, "max_shoe_investment")

# Optimize the model
m.optimize()

# Print the results
if m.status == GRB.OPTIMAL:
    print("Optimal solution found:")
    print(f"Amount invested in shoe factory: {x1.x}")
    print(f"Amount invested in hat factory: {x2.x}")
    print(f"Maximum earnings: {m.objVal}")
else:
    print("No optimal solution found")
```